预计阅读本页时间:-
Instances Versus Inheritance
Because metaclasses are specified in similar ways to inheritance superclasses, they can be a bit confusing at first glance. A few key points should help summarize and clarify the model:
广告:个人专属 VPN,独立 IP,无限流量,多机房切换,还可以屏蔽广告和恶意软件,每月最低仅 5 美元
- Metaclasses inherit from the type class. Although they have a special role, metaclasses are coded with class statements and follow the usual OOP model in Python. For example, as subclasses of type, they can redefine the type object’s methods, overriding and customizing them as needed. Metaclasses typically redefine the type class’s __new__ and __init__ to customize class creation and initialization, but they can also redefine __call__ if they wish to catch the end-of-class creation call directly. Although it’s unusual, they can even be simple functions that return arbitrary objects, instead of type subclasses.
- Metaclass declarations are inherited by subclasses. The metaclass=M declaration in a user-defined class is inherited by the class’s subclasses, too, so the metaclass will run for the construction of each class that inherits this specification in a superclass chain.
- Metaclass attributes are not inherited by class instances. Metaclass declarations specify an instance relationship, which is not the same as inheritance. Because classes are instances of metaclasses, the behavior defined in a metaclass applies to the class, but not the class’s later instances. Instances obtain behavior from their classes and superclasses, but not from any metaclasses. Technically, instance attribute lookups usually search only the __dict__ dictionaries of the instance and all its classes; the metaclass is not included in inheritance lookup.
To illustrate the last two points, consider the following example:
class MetaOne(type):
def __new__(meta, classname, supers, classdict): # Redefine type method
print('In MetaOne.new:', classname)
return type.__new__(meta, classname, supers, classdict)
def toast(self):
print('toast')
class Super(metaclass=MetaOne): # Metaclass inherited by subs too
def spam(self): # MetaOne run twice for two classes
print('spam')
class C(Super): # Superclass: inheritance versus instance
def eggs(self): # Classes inherit from superclasses
print('eggs') # But not from metclasses
X = C()
X.eggs() # Inherited from C
X.spam() # Inherited from Super
X.toast() # Not inherited from metaclass
When this code is run, the metaclass handles construction of both client classes, and instances inherit class attributes but not metaclass attributes:
In MetaOne.new: Super
In MetaOne.new: C
eggs
spam
AttributeError: 'C' object has no attribute 'toast'
Although detail matters, it’s important to keep the big picture in mind when dealing with metaclasses. Metaclasses like those we’ve seen here will be run automatically for every class that declares them. Unlike the helper function approaches we saw earlier, such classes will automatically acquire whatever augmentation the metaclass provides. Moreover, changes in such augmentation only need to be coded in one place—the metaclass—which simplifies making modifications as our needs evolve. Like so many tools in Python, metaclasses ease maintenance work by eliminating redundancy. To fully sample their power, though, we need to move on to some larger use-case examples.