This very short tutorial demonstrates how to use the abstract methods and contractual interfaces of Harold. Let us start with an example python module:
# circles.py from harold.prog import HaroldClass import harold.prog.abstract as abstract import math class CircleInterface (object): "A pure interface" __metaclass__ = HaroldClass @abstract.attr_r def radius (self): "The radius of this circle as a positive float." @abstract.attr_rw def center (self): "The tuple (x,y) representing this circle's center." @abstract.method def contains (self, x, y): "Does this circle contains point (x,y)" class CirclePartial (CircleInterface): "A partial (abstract) implementation of CircleInterface." def contains (self, x, y, boundary=True): xc,yc = self.center dx,dy = (xc-x,yc-y) d = math.sqrt (dx*dx+dy*dy) if boundary: return d <= self.radius else: return d < self.radius class CircleImplementation (CirclePartial): "This class is concrete" def __init__ (self, x, y, r): self._set_center ((x,y)) self._set_radius (r) def _set_center (self, (x,y)): self._x, self._y = x, y def _set_radius (self, r): if r < 0: raise Exception, "negative radius" self._r = r center = property ( lambda self: (self._x, self._y), _set_center, ) radius = property ( lambda self: self._r, _set_radius, )
This example contains three python classes, using the metaclass HaroldClass
(note that only CircleInterface
explicitly uses the metaclass; the others inherit it by subclassing the former):
CircleInterface
is a pure interface, since it does not implement any methods. Instead, all its methods and properties are abstract members, either read-only attribute (radius
), read-write attribute (center
) or method (contains
).CirclePartial
is a partial implementation of the above interface. It provides an implementation of the contains
method. It must be noted that, though this implementation accepts more arguments that the interface prototype, it is a valid implementation because it can be invoked with fewer arguments (thanks to the default value for the parameter boundary
). Overriding methods in a HaroldClass
must respect the interface of the overridden method by accepting at least the same range of arguments number (exactly 3 arguments, in that example). Failing to do that will issue a warning.CircleImplementation
is a concrete implementation of its superclass, since all remaining abstract members are now overridden by proper implementations. The contractual interface is extended since read-only attribute radius
is overridden by a read-write attribute. It would be erroneous, on the other hand, to override a read-write attribute by one which is read-only; that would issue a warning.
Executing the code above will generate the three classes. The first two will be recognized as abstract classes for they contain abstract members. All HaroldClass
's have an abstract
attribute indicating whether they are abstract or not, as well as an abstract_member
attribute containing the names of their abstract members. Trying to instantiate abstract classes will issue a warning, and should generally be avoided: the abstract members will raise exceptions whenever accessed. The third class above, CircleImplementation
can be instantiated normally, since it has no abstract member.
HaroldClass
It is possible to use some functionalities of Harold without making your classes instances of the metaclass HaroldClass
. For example, abstract members can be added to any python class. The difference is that
the class will not “know” that it is abstract, and will instantiate silently (without any warning). However, accessing the abstract members will still raise exceptions.
Though Harold aims to be as unintrusive as possible, by issuing warnings instead of exceptions in a number of situations, all the additional checks performed by HaroldClass
can be considered a waste of CPU time by people not interested in those functionalities. It is possible to disable them globally by changing HaroldClass
into the standard metaclass type
, thus turning Harold-aware libraries into lightweight library. The example below shows how:
import harold harold.lightweight = True # disable HaroldClass checks from circles import * c1 = CircleImplementation(0,0,1) c2 = CircleInterface() # this is not wise, # but it will work without any warning print c2.radius # this will however raise an exception
Not that harold.lightweight
must be set before any Harold-aware module is imported. This can also be done by defining the environment variable HAROLD_LIGHTWEIGHT
to any value. In the latter case, no additional python code is required.