Abstract methods in Python

Question:

I am having trouble in using inheritance with Python. While the concept seems too easy for me in Java yet up till now I have been unable to understand in Python which is surprising to me at least.

I have a prototype which follow:

class Shape():
   def __init__(self, shape_name):
       self.shape = shape_name

class Rectangle(Shape):
   def __init__(self, name):
       self.shape = name

In the above code how can I make an abstract method that would need to be implemented for all the subclasses?

Asked By: user506710

||

Answers:

See the abc module. Basically, you define __metaclass__ = abc.ABCMeta on the class, then decorate each abstract method with @abc.abstractmethod. Classes derived from this class cannot then be instantiated unless all abstract methods have been overridden.

If your class is already using a metaclass, derive it from ABCMeta rather than type and you can continue to use your own metaclass.

A cheap alternative (and the best practice before the abc module was introduced) would be to have all your abstract methods just raise an exception (NotImplementedError is a good one) so that classes derived from it would have to override that method to be useful.

However, the abc solution is better because it keeps such classes from being instantiated at all (i.e., it “fails faster”), and also because you can provide a default or base implementation of each method that can be reached using the super() function in derived classes.

Answered By: kindall

Something along these lines, using ABC

import abc

class Shape(object):
    __metaclass__ = abc.ABCMeta
    
    @abc.abstractmethod
    def method_to_implement(self, input):
        """Method documentation"""
        return
    

Also read this good tutorial: https://pymotw.com/3/abc/

You can also check out zope.interface which was used prior to introduction of ABC in python.

Answered By: pyfunc

You can’t, with language primitives. As has been called out, the abc package provides this functionality in Python 2.6 and later, but there are no options for Python 2.5 and earlier. The abc package is not a new feature of Python; instead, it adds functionality by adding explicit “does this class say it does this?” checks, with manually-implemented consistency checks to cause an error during initialization if such declarations are made falsely.

Python is a militantly dynamically-typed language. It does not specify language primitives to allow you to prevent a program from compiling because an object does not match type requirements; this can only be discovered at run time. If you require that a subclass implement a method, document that, and then just call the method in the blind hope that it will be there.

If it’s there, fantastic, it simply works; this is called duck typing, and your object has quacked enough like a duck to satisfy the interface. This works just fine even if self is the object you’re calling such a method on, for the purposes of mandatory overrides due to base methods that need specific implementations of features (generic functions), because self is a convention, not anything actually special.

The exception is in __init__, because when your initializer is being called, the derived type’s initializer hasn’t, so it hasn’t had the opportunity to staple its own methods onto the object yet.

If the method was’t implemented, you’ll get an AttributeError (if it’s not there at all) or a TypeError (if something by that name is there but it’s not a function or it didn’t have that signature). It’s up to you how you handle that- either call it programmer error and let it crash the program (and it “should” be obvious to a python developer what causes that kind of error there- an unmet duck interface), or catch and handle those exceptions when you discover that your object didn’t support what you wish it did. Catching AttributeError and TypeError is important in a lot of situations, actually.

Answered By: Adam Norberg

Before abc was introduced you would see this frequently.

class Base(object):
    def go(self):
        raise NotImplementedError("Please Implement this method")


class Specialized(Base):
    def go(self):
        print "Consider me implemented"
Answered By: kevpie

Abstract base classes are deep magic. Periodically I implement something using them and am amazed at my own cleverness, very shortly afterwards I find myself completely confused by my own cleverness (this may well just be a personal limitation though).

Another way of doing this (should be in the python std libs if you ask me) is to make a decorator.

def abstractmethod(method):
    """
    An @abstractmethod member fn decorator.
    (put this in some library somewhere for reuse).

    """
    def default_abstract_method(*args, **kwargs):
        raise NotImplementedError('call to abstract method ' 
                                  + repr(method))
    default_abstract_method.__name__ = method.__name__    
    return default_abstract_method


class Shape(object):

    def __init__(self, shape_name):
       self.shape = shape_name

    @abstractmethod
    def foo(self):
        print "bar"
        return

class Rectangle(Shape):
    # note you don't need to do the constructor twice either
    pass  

r = Rectangle("x")
r.foo()

I didn’t write the decorator. It just occurred to me someone would have. You can find it here: http://code.activestate.com/recipes/577666-abstract-method-decorator/ Good one jimmy2times. Note the discussion at the bottom of that page r.e. type safety of the decorator. (That could be fixed with the inspect module if anyone was so inclined).

Answered By: demented hedgehog

You can use six and abc to construct a class for both python2 and python3 efficiently as follows:

import six
import abc

@six.add_metaclass(abc.ABCMeta)
class MyClass(object):
    """
    documentation
    """

    @abc.abstractmethod
    def initialize(self, para=None):
        """
        documentation
        """
        raise NotImplementedError

This is an awesome document of it.

Answered By: Lerner Zhang
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.