5.4. MetaclassesAny object, even a class object, has a type. In Python, types and classes are also first-class objects. The type of a class object is also known as the class's metaclass.[*] An object's behavior is mostly determined by the type of the object. This also holds for classes: a class's behavior is mostly determined by the class's metaclass. Metaclasses are an advanced subject, and you may want to skip the rest of this section on first reading. However, fully grasping metaclasses can help you obtain a deeper understanding of Python, and occasionally it can be useful to define your own custom metaclasses.
The distinction between legacy and new-style classes relies on the fact that each class's behavior is determined by its metaclass. In other words, the reason legacy classes behave differently from new-style classes is that legacy and new-style classes are objects of different types (metaclasses): class Classic: pass class Newstyle(object): pass print type(Classic) # prints: <type 'class'> print type(Newstyle) # prints: <type 'type'> The type of Classic is object types.ClassType from standard module types, while the type of Newstyle is built-in object type. type is also the metaclass of all Python built-in types, including itself (i.e., print type(type) also prints <type 'type'>). 5.4.1. How Python Determines a Class's MetaclassTo execute a class statement, Python first collects the base classes into a tuple t (an empty one if there are no base classes) and executes the class body in a temporary dictionary d. Then, Python determines the metaclass M to use for the new class object C that the class statement is creating. When '_ _metaclass_ _' is a key in d, M is d['_ _metaclass_ _']. Thus, you can explicitly control class C's metaclass by binding the attribute _ _metaclass_ _ in C's class body. Otherwise, when t is nonempty (i.e., when C has one or more base classes), M is the leafmost metaclass among all of the metaclasses of C's bases.[*] This is why inheriting from object indicates that C is a new-style class. Since type(object) is type, a class C that inherits from object (or some other built-in type) gets the same metaclass as object (i.e., type(C), C's metaclass, is also type). Thus, being a new-style class is synonymous with having type as the metaclass.
When C has no base classes, but the current module has a global variable _ _metaclass_ _, M is the value of that global variable. This lets you make classes without base classes default to new-style classes, rather than legacy classes, throughout a module. Just place the following statement toward the start of the module body: _ _metaclass_ _ = type Failing all of these, M defaults to types.ClassType. This last "default of defaults" clause is why classes without base classes are legacy by default, when _ _metaclass_ _ is not bound in the class body or as a global variable of the module. 5.4.2. How a Metaclass Creates a ClassHaving determined M, Python calls M with three arguments: the class name (a string), the tuple of base classes t, and the dictionary d. The call returns the class object C, which Python then binds to the class name, completing the execution of the class statement. Note that this is in fact an instantiation of type M, so the call to M executes M._ _init_ _(C, namestring, t, d), where C is the return value of M._ _new_ _(M, namestring, t, d), just as in any other similar instantiation of a new-style class. After class object C is created, the relationship between class C and its type (type(C), normally M) is the same as that between any object and its type. For example, when you call the class object C (to create an instance of C), M._ _call_ _ executes with class object C as the first actual argument. Note the benefit of the new-style approach described in "Per-Instance Methods" on page 103. Calling C to instantiate it must execute the metaclass's M._ _call_ _, whether or not C has a per-instance attribute (method) _ _call_ _ (i.e., independently of whether instances of C are or aren't callable). This requirement is simply incompatible with the legacy object model, where per-instance methods override per-class oneseven for implicitly called special methods. The new-style approach avoids having to make the relationship between a class and its metaclass an ad hoc special case. Avoiding ad hoc special cases is a key to Python's power: Python has few, simple, general rules, and applies them consistently. 5.4.2.1. Defining and using your own metaclassesIt's easy to define custom metaclasses: inherit from type and override some of its methods. You can also perform most of these tasks with _ _new_ _, _ _init_ _, _ _getattribute_ _, and so on without involving metaclasses. However, a custom metaclass can be faster, since special processing is done only at class creation time, which is a rare operation. A custom metaclass lets you define a whole category of classes in a framework that magically acquires whatever interesting behavior you've coded, quite independently of what special methods the classes may choose to define. Moreover, some behavior of class objects can be customized only in metaclasses. The following example shows how to use a metaclass to change the string format of class objects: class MyMeta(type): def _ _str_ _(cls): return "Beautiful class '%s'"%cls._ _name_ _ class MyClass: _ _metaclass_ _ = MyMeta x = MyClass( ) print type(x) # emits: Beautiful class 'MyClass' Strictly speaking, classes that instantiate such a custom metaclass are neither classic nor new-style: the semantics of classes and of their instances are entirely defined by their metaclass. In practice, custom metaclasses almost invariably subclass built-in type. Therefore, the semantics of the classes that instantiate such custom metaclasses are best thought of as variations on the semantics of new-style classes. 5.4.2.2. A substantial custom metaclass exampleSuppose that, programming in Python, we miss C's struct type: an object that is just a bunch of data attributes with fixed names. Python lets us easily define an appropriate generic Bunch class, apart from the fixed names: class Bunch(object): def _ _init_ _(self, **fields): self._ _dict_ _ = fields p = Bunch(x=2.3, y=4.5) print p # prints: <_ _main_ _.Bunch object at 0x00AE8B10> However, a custom metaclass lets us exploit the fact that the attribute names are fixed at class creation time. The code shown in Example 5-1 defines a metaclass, metaMetaBunch, and a class, MetaBunch, that let us write code like the following: class Point(MetaBunch): """ A point has x and y coordinates, defaulting to 0.0, and a color, defaulting to 'gray' -- and nothing more, except what Python and the metaclass conspire to add, such as _ _init_ _ and _ _repr_ _ """ x = 0.0 y = 0.0 color = 'gray' # example uses of class Point q = Point( ) print q # prints: Point( ) p = Point(x=1.2, y=3.4) print p # prints: Point(y=3.399999999, x=1.2) In this code, the print statements print readable string representations of our Point instances. Point instances are quite memory-lean, and their performance is basically the same as for instances of the simple class Bunch in the previous example (there is no extra overhead due to special methods getting called implicitly). Note that Example 5-1 is quite substantial, and following all its details requires understanding aspects of Python covered later in this book, such as strings (Chapter 9) and module warnings ("The warnings Module" on page 471). The identifier mcl used in Example 5-1 stands for "metaclass," a use that is clearer in this special advanced case than the more habitual case of cls standing for "class." Example 5-1. The metaMetaBunch metaclass
![]() |