The point is that python doesn't have a notion of abstract methods. Abstract methods are part of an base class that defines an interface, without any code. Abstract methods can't be called directly, because they don't contain any code in their definition.
In the definition of the base class, you may want to include a specific method that is part of the interface, but the specific implementation is still unknown. A popular example seems to be the drawing of a point or a line in a graphical application.
The classes Point and Line share several implementation details, but differ on other. In particular, the way they are drawn is completely different (you will want to optimize the drawing of a line). Suppose these two classes are derived from the same class, Object. It is possible to separate the implementation of the method draw of these two classes, while draw can still be called from the base class Object.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
class AbstractMethod (object): """Defines a class to create abstract methods @example: class Foo: foo = AbstractMethod('foo') """ def __init__(self, func): """Constructor @params func: name of the function (used when raising an exception). @type func: str """ self._function = func def __get__(self, obj, type): """Get callable object @returns An instance of AbstractMethodHelper. This trickery is needed to get the name of the class for which an abstract method was requested, otherwise it would be sufficient to include a __call__ method in the AbstractMethod class itself. """ return self.AbstractMethodHelper(self._function, type) class AbstractMethodHelper (object): """Abstract method helper class An AbstractMethodHelper instance is a callable object that represents an abstract method. """ def __init__(self, func, cls): self._function = func self._class = cls def __call__(self, *args, **kwargs): """Call abstract method Raises a TypeError, because abstract methods can not be called. """ raise TypeError('Abstract method `' + self._class.__name__ \ + '.' + self._function + '\' called') class Metaclass (type): def __init__(cls, name, bases, *args, **kwargs): """Configure a new class @param cls: Class object @param name: Name of the class @param bases: All base classes for cls """ super(Metaclass, cls).__init__(cls, name, bases, *args, **kwargs) # Detach cls.new() from class Metaclass, and make it a method # of cls. cls.__new__ = staticmethod(cls.new) # Find all abstract methods, and assign the resulting list to # cls.__abstractmethods__, so we can read that variable when a # request for allocation (__new__) is done. abstractmethods =  ancestors = list(cls.__mro__) ancestors.reverse() # Start with __builtin__.object for ancestor in ancestors: for clsname, clst in ancestor.__dict__.items(): if isinstance(clst, AbstractMethod): abstractmethods.append(clsname) else: if clsname in abstractmethods: abstractmethods.remove(clsname) abstractmethods.sort() setattr(cls, '__abstractmethods__', abstractmethods) def new(self, cls): """Allocator for class cls @param self: Class object for which an instance should be created. @param cls: Same as self. """ if len(cls.__abstractmethods__): raise NotImplementedError('Can\'t instantiate class `' + \ cls.__name__ + '\';\n' + \ 'Abstract methods: ' + \ ", ".join(cls.__abstractmethods__)) return object.__new__(self) class Object (object): __metaclass__ = Metaclass draw = AbstractMethod('draw') def update(self): self.draw() class Point (Object): def draw(self): print 'Point.draw called' >>> Point().update() Point.draw called >>> Object().update() NotImplementedError: Can't instantiate class `Object'; Abstract methods: draw
A bit more explanation about why some things had to be done is in the article I originally wrote to introduce this code: http://www.lychnis.net/index/programming/python-abstract-methods-3.lychnis
I don't understand. I'm a Python newbie; excuse me if don't get it right but i thought Python was about simplicity ...
What's wrong with:
Complexity. For one thing: clarity. My version gives the exact class and method names where the abstract method was defined.
Second: certainty. With that metaclass, it is guaranteed that you will never accitentally instantiate a class with one or more abstract classes.
Constructor of subclasses requires 2 arguments. I have created an abstract class which (besides self) has one argument on the __init__ method.
Later, I've created a subclass of that abstract one, which added one more parameter to the __init__.
When creating an instance of the later, the error below pops up:
Am I restricted to __init__(self) only (no other parameters)?
Nice, while it is not amazingly clean it is great if you don't want somebody to instantiate an abstract class...This is probably the simplest recipe for creating an abstract class I have seen so far...can't wait for python 3000 when interfaces and abstract stuff is supported natively!!!
By the way if you want to have parameters in the __init__ of sub classes you just need to change line 80 for new to
Also, I noticed that you really don't need to pass the name of the function to instantiate AbstractMethod...maybe it was needed in previous versions of python but 2.5 works without it. I changed the __init__ for AbstractMethod to have func=None. That way in my abstract class I just do
that way it is not as redundant with passing the name again to the AbstractMethod. It still spits out the name when it says that I can't instantiate a class with abstract methods.