The package Properties defines one module, PropertiedClasses. PropertiedClasses is a module that defines a mix-in class PropertiedClass. Classes that inherit it appear have what appear to be attributes that are actually read, written or deleted via functions.
A property y in a class C is defined by associating the name y to functions that respond to one or more of the three possible operations on attributes:
The association is performed by calls to PropertiedClasses.set_property as explained below. We refer to these functions as the "handlers" for the property. In defining a property, you can specify one, two, or three of the functions above. If you do not specify an attribute, the normal attribute fetch mechanism can be used (which means that self.__dict__[name] is returned, set, or deleted, respectively).
Additionally, set_property allows you to use a short-hand method of arranging for the attribute to be unwriteable and/or undeleteable via the normal mechanisms.
There are two ways to avoid the property mechanism, which you may want to do, for example, to set a value to a "unwriteable" attribute.
First, PropertiedClass defines a method is_internal_attribute (self, name) which may be redefined in a child. The default version is to true if the name begins with an underscore. Names for which
is_internal_attribute(self, name) is true are always treated as names to which the property mechanism does not apply, and no attempt is made to locate "handlers" for them.
Secondly, methods _basic_get (self, name), _basic_set (self, name, value), and _basic_del (self, name) are available to perform the "normal" get, set, and delete functions via the instance dictionary, self.__dict__.
In defining a class that will have properties, all you have to do is inherit from PropertiedClass. You also need to exercise great caution in overriding any of the three special attributes related to attribute handling, __getattr__, __setattr__, and __delattr__, since PropertiedClass defines these in order to do its job. The property behavior can be lost if any child's versions do not call the parent versions of these routines.
In writing the methods of your class, be aware that the properties you define will be active even in the initialization routine for the class. You may in particular need to use _basic_set to create the initial state.
The module function set_property should be called immediately after the class definition to set properties:
set_property (C, name, actg=None, acts=None, actd=None,
"""Set attribute handlers for name to methods actg, acts, actd
None means no change for that action.
nowrite = 1 prevents setting this attribute.
nodelete = 1 prevents deleting this attribute.
nodelete defaults to 1 unless actd given.
if nowrite and nodelete is None: nodelete = 1
C is the class, name is the name of the property to define, and actg, acts, and actd are unbound methods of class C having signatures actg (self, name), acts (self, name, value), and actd (self, name). The specifying of nowrite = 1 causes acts to be the method PropertiedClass._write_disallowed, a function that will raise an AttributeError if invoked. If nodelete is 1, actd is set to PropertiedClass._delete_disallowed, a function that will raise an AttributeError if invoked. It is an error to specify nowrite or nodelete at the same time you specify acts or actd respectively. You can use the routine _basic_get for actg, _basic_set for acts, or _basic_del for actd, if you wish.
In this example, three properties are defined: a is a normal attribute but it cannot be deleted; b, an undeleteable property which gets and sets a hidden value _b; and c, an undeleteable attribute whose value is checked on assignment to make sure it is greater than zero. The attribute _d is unaffected by Properties since its name starts with an underscore.
class C (PropertiedClasses.PropertiedClass):
def assign_with_validate (self, name, value):
"Checks that name is positive before assignment."
if c <= 0: raise ValueError, `cannot assign negative value'
def _bset (self, name, value):
PropertiedClasses.set_property (C, `a', nodelete=1)
PropertiedClasses.set_property (C, `b', C._bget, C._bset)
PropertiedClasses.set_property (C, `c', acts = C.assign_with_validate)
Note that as written the method assign_with_validate will only be called internally with the name `c'. If we latter add another attribute c2 that we also wish to validate with this method, we can use the same method as acts for it.