Time for action using metaclasses

Say we want to be able to verify that classes we define always have an __info__() method. We can accomplish that by defining a suitable metaclass and defining any new class that should be checked with a reference to this metaclass. Look at the following example code (also available as metaclassexample.py):

Chapter7/metaclassexample.py

class hasinfo(type):
	def __new__(metaclass, classname, baseclasses, classdict):
		if len(baseclasses) and not '__info__' in classdict:
			raise TypeError('does not have __info__')
		return type.__new__(metaclass,
				classname, baseclasses, classdict)
class withinfo(metaclass=hasinfo):
	pass
class correct(withinfo):
	def __info__(self): pass
class incorrect(withinfo):
	pass

This will raise an exception for the incorrect class, but not for the correct class.

What just happened?

As you can see, the __new__() method of a metaclass receives a number of important parameters. First, the metaclass itself and the classname of the class that is being defined, a (possibly empty) list of baseclasses, and finally the class dictionary. That last argument is very important.

As we define a class with a class statement, all methods and class variables we define here end up in a dictionary. Once this class is completely defined, this dictionary will be available as the __dict__ attribute of the class. This is the dictionary that is passed to the __new__() method of the metaclass and we can examine and alter this dictionary at will.

In this example, we simply check whether there exists a key called __info__ in this class dictionary and raise an exception if it doesn't. (We do not really check that it is a method but this is possible too of course). If everything went well, we call the __new__() method of type (the mother of all metaclasses) as that method will take care of making the class definition available in the current scope.

There is an extra trick involved, however. The withinfo class is abstract in the sense that it defines the need for an __info__() method by referring to the hasinfo metaclass, but it does not define one itself. However, because it refers to the hasinfo metaclass, an exception would be raised because its own class dictionary is checked in the same way as its subclasses. To prevent this, we only check for the occurrence of the __info__() method if a class is a subclass, that is, when the list of base classes (available in the baseclasses parameter) is not empty.

Checking for mandatory methods is nice, but with so much information available, much more can be done. In the next section, we use this power to ensure that the definition of a new class will take care of creating suitable tables in a database backend as well.

MetaEntity and AbstractEntity classes

Besides creating a database table, if necessary, the metaclass that we will define will also examine the Attribute instance assigned to any class variable to build dictionaries of display names and validation functions. This way, subclasses can easily check if a column has such an attribute by using the column name as a key, thus obviating the need to check all class variables themselves again and again.

MetaEntity and AbstractEntity classes
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.137.167.195