预计阅读本页时间:-
Metaclasses Versus Class Decorators: Round 1
Having said that, it’s also interesting to note that the class decorators described in the preceding chapter sometimes overlap with metaclasses in terms of functionality. Although they are typically used for managing or augmenting instances, class decorators can also augment classes, independent of any created instances.
For example, suppose we coded our manager function to return the augmented class, instead of simply modifying it in-place. This would allow a greater degree of flexibility, because the manager would be free to return any type of object that implements the class’s expected interface:
广告:个人专属 VPN,独立 IP,无限流量,多机房切换,还可以屏蔽广告和恶意软件,每月最低仅 5 美元
def extra(self, arg): ...
def extras(Class):
if required():
Class.extra = extra
return Class
class Client1: ...
Client1 = extras(Client1)
class Client2: ...
Client2 = extras(Client2)
class Client3: ...
Client3 = extras(Client3)
X = Client1()
X.extra()
If you think this is starting to look reminiscent of class decorators, you’re right. In the prior chapter we presented class decorators as a tool for augmenting instance creation calls. Because they work by automatically rebinding a class name to the result of a function, though, there’s no reason that we can’t use them to augment the class before any instances are ever created. That is, class decorators can apply extra logic to classes, not just instances, at creation time:
def extra(self, arg): ...
def extras(Class):
if required():
Class.extra = extra
return Class
@extras
class Client1: ... # Client1 = extras(Client1)
@extras
class Client2: ... # Rebinds class independent of instances
@extras
class Client3: ...
X = Client1() # Makes instance of augmented class
X.extra() # X is instance of original Client1
Decorators essentially automate the prior example’s manual name rebinding here. Just like with metaclasses, because the decorator returns the original class, instances are made from it, not from a wrapper object. In fact, instance creation is not intercepted at all.
In this specific case—adding methods to a class when it’s created—the choice between metaclasses and decorators is somewhat arbitrary. Decorators can be used to manage both instances and classes, and they intersect with metaclasses in the second of these roles.
However, this really addresses only one operational mode of metaclasses. As we’ll see, decorators correspond to metaclass __init__ methods in this role, but metaclasses have additional customization hooks. As we’ll also see, in addition to class initialization, metaclasses can perform arbitrary construction tasks that might be more difficult with decorators.
Moreover, although decorators can manage both instances and classes, the converse is not as direct—metaclasses are designed to manage classes, and applying them to managing instances is less straightforward. We’ll explore this difference in code later in this chapter.
Much of this section’s code has been abstract, but we’ll flesh it out into a real working example later in this chapter. To fully understand how metaclasses work, though, we first need to get a clearer picture of their underlying model.