Online Book Reader

Home Category

Learning Python - Mark Lutz [569]

By Root 1858 0
taps into the metaclass hook to customize—because the metaclass is called at the end of a class statement, and because the type object’s __call__ dispatches to the __new__ and __init__ methods, code we provide in these methods can manage all the classes created from the metaclass.

Here’s our example in action again, with prints added to the metaclass and the file at large to trace:

class MetaOne(type):

def __new__(meta, classname, supers, classdict):

print('In MetaOne.new:', classname, supers, classdict, sep='\n...')

return type.__new__(meta, classname, supers, classdict)

class Eggs:

pass

print('making class')

class Spam(Eggs, metaclass=MetaOne): # Inherits from Eggs, instance of Meta

data = 1 # Class data attribute

def meth(self, arg): # Class method attribute

pass

print('making instance')

X = Spam()

print('data:', X.data)

Here, Spam inherits from Eggs and is an instance of MetaOne, but X is an instance of and inherits from Spam. When this code is run with Python 3.0, notice how the metaclass is invoked at the end of the class statement, before we ever make an instance—metaclasses are for processing classes, and classes are for processing instances:

making class

In MetaOne.new:

...Spam

...(,)

...{'__module__': '__main__', 'data': 1, 'meth': }

making instance

data: 1

Customizing Construction and Initialization

Metaclasses can also tap into the __init__ protocol invoked by the type object’s __call__: in general, __new__ creates and returns the class object, and __init__ initializes the already created class. Metaclasses can use both hooks to manage the class at creation time:

class MetaOne(type):

def __new__(meta, classname, supers, classdict):

print('In MetaOne.new: ', classname, supers, classdict, sep='\n...')

return type.__new__(meta, classname, supers, classdict)

def __init__(Class, classname, supers, classdict):

print('In MetaOne init:', classname, supers, classdict, sep='\n...')

print('...init class object:', list(Class.__dict__.keys()))

class Eggs:

pass

print('making class')

class Spam(Eggs, metaclass=MetaOne): # Inherits from Eggs, instance of Meta

data = 1 # Class data attribute

def meth(self, arg): # Class method attribute

pass

print('making instance')

X = Spam()

print('data:', X.data)

In this case, the class initialization method is run after the class construction method, but both run at the end of the class statement before any instances are made:

making class

In MetaOne.new:

...Spam

...(,)

...{'__module__': '__main__', 'data': 1, 'meth': }

In MetaOne init:

...Spam

...(,)

...{'__module__': '__main__', 'data': 1, 'meth': }

...init class object: ['__module__', 'data', 'meth', '__doc__']

making instance

data: 1

Other Metaclass Coding Techniques

Although redefining the type superclass’s __new__ and __init__ methods is the most common way metaclasses insert logic into the class object creation process, other schemes are possible, too.

Using simple factory functions

For example, metaclasses need not really be classes at all. As we’ve learned, the class statement issues a simple call to create a class at the conclusion of its processing. Because of this, any callable object can in principle be used as a metaclass, provided it accepts the arguments passed and returns an object compatible with the intended class. In fact, a simple object factory function will serve just as well as a class:

# A simple function can serve as a metaclass too

def MetaFunc(classname, supers, classdict):

print('In MetaFunc: ', classname, supers, classdict, sep='\n...')

return type(classname, supers, classdict)

class Eggs:

pass

print('making class')

class Spam(Eggs, metaclass=MetaFunc): # Run simple function at end

data = 1 # Function returns class

def meth(self, args):

pass

print('making instance')

X = Spam()

print('data:', X.data)

When run, the function is called

Return Main Page Previous Page Next Page

®Online Book Reader