作为一个具体的应用例子,下面定义了一个元类,它会拒绝任何有混合大小写名字作为方法的类定义
class NoMixedCaseMeta(type): def new(cls, clsname, bases, clsdict): for name in clsdict: if name.lower() != name: raise TypeError('Bad attribute name: ' name) return super().new(cls, clsname, bases, clsdict)
class Root(metaclass=NoMixedCaseMeta): pass
class A(Root): def foo_bar(self): # Ok pass
class B(Root): def fooBar(self): # TypeError pass
检查元类的签名
from inspect import signature import logging
class MatchSignaturesMeta(type):
代码语言:javascript复制def __init__(self, clsname, bases, clsdict):
super().__init__(clsname, bases, clsdict)
sup = super(self, self)
for name, value in clsdict.items():
if name.startswith('_') or not callable(value):
continue
# Get the previous definition (if any) and compare the signatures
prev_dfn = getattr(sup,name,None)
if prev_dfn:
prev_sig = signature(prev_dfn)
val_sig = signature(value)
if prev_sig != val_sig:
logging.warning('Signature mismatch in %s. %s != %s',
value.__qualname__, prev_sig, val_sig)
Example
class Root(metaclass=MatchSignaturesMeta): pass
class A(Root): def foo(self, x, y): pass
代码语言:javascript复制def spam(self, x, *, z):
pass
Class with redefined methods, but slightly different signatures
class B(A): def foo(self, a, b): pass
代码语言:javascript复制def spam(self,x,z):
pass
在元类中选择重新定义 new() 方法还是 init() 方法取决于你想怎样使用结果类。 new() 方法在类创建之前被调用,通常用于通过某种方式(比如通过改变类字典的内容)修改类的定义。 而 init() 方法是在类被创建之后被调用,当你需要完整构建类对象的时候会很有用。 在最后一个例子中,这是必要的,因为它使用了 super() 函数来搜索之前的定义。 它只能在类的实例被创建之后,并且相应的方法解析顺序也已经被设置好了。
最后一个例子还演示了Python的函数签名对象的使用。 实际上,元类将每个可调用定义放在一个类中,搜索前一个定义(如果有的话), 然后通过使用 inspect.signature() 来简单的比较它们的调用签名。