我想装饰我班上所有的方法。我在这里写了一个小小的装饰器示例用于说明。
装饰器:
def debug(func):
msg = func.__name__
@wraps(func)
def wrapper(*args, **kwargs):
print(msg)
return func(*args, **kwargs)
return wrapper
def debugmethods(cls):
for key, val in vars(cls).items():
if callable(val):
setattr(cls, key, debug(val))
return cls
现在,我想装饰我班上的所有方法。一种简单的方法是在类的顶部使用@debugmethods批注,但我正在尝试了解另外两种不同的方法。
a)覆盖__new__
class Spam:
def __new__(cls, *args, **kwargs):
clsobj = super().__new__(cls)
clsobj = debugmethods(clsobj)
return clsobj
def __init__(self):
pass
def foo(self):
pass
def bar(self):
pass
spam = Spam()
spam.foo()
b)编写元类
class debugmeta(type):
def __new__(cls, clsname, bases, clsdict):
clsobj = super().__new__(cls, clsname, bases, clsdict)
clsobj = debugmethods(clsobj)
return clsobj
class Spam(metaclass = debugmeta):
def foo(self):
pass
def bar(self):
pass
spam = Spam()
spam.foo()
我不确定
__new__
”不起作用?__new__
的签名在元类中不同? 有人可以帮助我了解我在这里想念的是什么。
答案 0 :(得分:2)
您似乎对__new__
和元类感到困惑。调用__new__
来创建一个新对象(一个类的实例,一个元类的类),它不是一个“创建的类”钩子。
正常模式是:
Foo(...)
已翻译为type(Foo).__call__(Foo, ...)
,请参阅{{3}}。类的type()
是它的元类。在type.__call__
是自定义Python类时使用的标准Foo
实现将调用__new__
创建一个新实例,然后在该实例上调用__init__
方法如果结果确实是Foo
类的实例,则为instance:
def __call__(cls, *args, **kwargs): # Foo(...) -> cls=Foo
instance = cls.__new__(cls, *args, **kwargs) # so Foo.__new__(Foo, ...)
if isinstance(instance, cls):
instance.__init__(*args, **kwargs) # Foo.__init__(instance, ...)
return instance
因此,仅在创建Foo.__new__
的实例时,才在创建Foo
类本身时不调用Foo
。
您通常不需要在类中使用__new__
,因为__init__
足以初始化实例的属性。但是对于不可变类型,例如int
或tuple
,您只能使用__new__
来准备新的实例状态,因为您不能更改的属性一旦创建了一个不变的对象。如果您想更改__new__
产生哪种类型的实例(例如创建单例或代替生成专门的子类),ClassObj()
也很有帮助。
相同的__call__
-> __new__
以及也许__init__
过程适用于元类。通过调用元类创建一个类对象来实现class Foo: ...
语句,该类对象通常作为字典传入3个参数:类名,类基数和类主体。对于class Spam(metaclass = debugmeta): ...
,这意味着debugmeta('Spam', (), {...})
被调用,这意味着debugmeta.__new__(debugmeta, 'Spam', (), {...})
被调用。
您的第一次尝试 a 设置Spam.__new__
不起作用,因为您在那里没有创建类对象。相反,super().__new__(cls)
创建一个没有属性的空Spam()
实例,因此vars()
返回一个空字典,而debugmethods()
最终什么也不做。>
如果您想参与类的创建,那么就需要一个元类。