Package harold :: Package prog :: Package abstract
[show private | hide private]
[frames | no frames]

Package harold.prog.abstract

Abstract classes for python.

Rationale

It is common in object oriented languages to define a class which provides common methods to several subclasses, but is not supposed to be instanciated, generally because it is missing some methods. Such a class is known as an abstract class.

Being weakly typed, Python does not provide any explicit construct for abstract classes. Indeed, so called abstract classes can be instanciated and this will not even be a problem as long as missing methods are not invoked. The drawback of this flexibility is that when such missing method is invoked, an exception is raised at a point of the program where it can be quite difficult to find the faulty instanciation.

This package provides an infrastructure allowing to explicitely define abstract (i.e. missing) members, which has three advantages, without losing Python flexibility: The use of Python's warnings allows developers to fine tune the behaviour of this package when an abstract class is instanciated.

Abstract members

The package implements the notion of abstract member, i.e. a method or property which is not implemented by the class and has to be implemented by a subclass.

abstract.attr_r and abstract.attr_rw can be used in class declaration to provide the semantics of an abstract readable (resp. readable and writable) attribute. They can be used as decorators of a dummy method named after the attribute, whose docstring will be used as the docstring of the attribute (it may seem strange at first sight to declare attributes as methods, but it gives an homogeneous and nice look to abstract classes, and makes the setting of a docstring more convenient).

abstract.method can be used as a function decorator to provide the semantics of an abstract method.

Any attemps to access an abstract attribute will raise AbstractAttributeException. Any attempt to invoke an abstract method will raise an AbstractMethodInvocationException.

See harold.prog.abstract.member

Abstract members and the MRO

It must be noted that python's MRO (method resolution order) influences the way abstract methods are to be used. Consider the following example:
   class AbstractClass (object):
       __metaclass__ = AbstractableClass
       @abstract.method
       def m (self, x): pass

   class ConcreteClass (object):
       def m (self, x): return x

   class CorrectInheritance (ConcreteClass, AbstractClass):
       pass

   class MisleadingInheritance (AbstractClass, ConcreteClass):
       pass

The class CorrectInheritance is concrete, since the MRO resolves method m as its concrete definition in ConcreteClass. On the other hand, MisleadingInheritance is abstract because according to the MRO, AbstractMethod.m overrides ConcreteMethod.m.

As a rule of thumb, it is always a good idea to put concrete classes first and abstract classes last in the superclasses list. When using AbstractableClass (see below), failing to do so will issue an {AbstractOverridesConcreteWarning<harold.prog.abstract.exceptions.AbstractOverridesConcreteWarning>}.

Abstractable class

This package also provides the AbstractableClass metaclass. Any class of this metaclass has a readonly attribute abstract, indicating if it contains or inherits non-overridden abstract members or cycles. In this case, it will also have read-only attributes abstract_members and abstract_cycles.

Furthermore, instantiating an abstract AbstractableClass will issue an AbstractClassInstantiationWarning, unless it has a default implementation (see.below).

Abstract cycles

The package introduces the notion of abstract cyle. Let us take an example:
   class Circle:
     def radius (self):
         return self.diameter()/2
     def diameter (self):
         return self.perimeter()/math.pi
     def perimeter (self):
         return self.radius()*math.pi*2

That class has no abstract method, since all are implemented. However, it is abstract, since none of its method will work properly (they will all result in a stack overflow) unless at least one of them is overridden. Those three methods are all implemented, but they form an abstract cycle (or dependancy cycle) which must be broken by changing one of the implementations.

Each abstract cycle is declared as a tuple of methods which form the cycle. All abstract cycles are declared as a list in a class attribute named __abstract_cycles__. Abstract cycles will not prevent the methods to be called, but an AbstractableClass containing abstract cycles will recognizes itself as abstract, just as if it contained an abstract member.

Default implementation

Abstractable classes have an attribute default_implementation. If the class is concrete (i.e. if its attribute abstract is false), the attribute is read-only and equals the class. It the class is abstract, the attribute is read-write. Any concrete subclass of the abstract class can be set as its default implementation. Any subsequent attempt to instantiate the abstract class will return an instance of the subclass. This replaces elegantly the factory idiom encountered in other languages.

Finally, the harold.prog.abstract package can automatically set the default_implementation of abstract classes whenever a concrete subclass is defined, if the abstract class has no default implementation yet. This is enabled by the autoset_default_implementation function. This behaviour is disabled by default: since its consequences might be quite unexpected for someone unaware of this functionnality, it has to be explicitely enabled.

See harold.prog.abstract.abstractable_class
Submodules

Generated by Epydoc 2.1 on Mon Dec 18 15:25:58 2006 http://epydoc.sf.net