通过覆盖 __new__ 来装饰 class 方法不起作用?
Decorating class methods by overriding __new__ doesn't work?
我想装饰我的class的所有方法。我在这里写了一个示例小装饰器用于说明目的。
装饰者:
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
现在我想装饰我的class的所有方法。一种简单的方法是在我的 class 之上使用 @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
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()
我不确定
- 为什么“a) 覆盖
__new__
”不起作用?
- 为什么方法
__new__
的签名在 metaclass 中不同?
有人可以帮助我了解我在这里缺少什么。
您似乎混淆了 __new__
和 metaclasses。调用 __new__
来创建一个新对象(来自 class 的实例,来自 metaclass 的 class),它不是 'class created' 钩子。
正常模式是:
Foo(...)
被翻译成 type(Foo).__call__(Foo, ...)
,请参阅 special method lookups 了解原因。 class 的 type()
是元 class.
当Foo
是自定义的Python时使用的标准type.__call__
实现class将调用__new__
创建一个新实例, 如果结果确实是 Foo
class:
的实例,则对该实例调用 __init__
方法
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
class 本身被创建时 Foo.__new__
不会被调用,只有当 Foo
的 实例 是已创建。
通常不需要在classes中使用__new__
,因为__init__
足以初始化实例的属性。但是对于 immutable 类型,比如 int
或 tuple
,你只能使用 __new__
来准备新的实例状态,因为你不能改变不可变对象创建后的属性。 __new__
在您想要更改 ClassObj()
生成的实例类型时也很有用(例如创建单例或生成专门的子 classes)。
相同的 __call__
-> __new__
并且可能 __init__
过程适用于 metaclasses。 class Foo: ...
语句是通过调用 metaclass 创建一个 class 对象来实现的,传入 3 个参数:class 名称,class 基数,以及class 正文,通常作为字典。使用 class Spam(metaclass = debugmeta): ...
,这意味着 debugmeta('Spam', (), {...})
被调用,这意味着 debugmeta.__new__(debugmeta, 'Spam', (), {...})
被调用。
您的第一次尝试 a,设置 Spam.__new__
无效,因为您 没有创建 class 对象 那里。相反,super().__new__(cls)
创建一个没有属性的空 Spam()
实例 ,因此 vars()
returns 一个空字典和 debugmethods()
最终什么都不做。
如果你想挂钩到 class 创建,那么你需要一个 metaclass.
我想装饰我的class的所有方法。我在这里写了一个示例小装饰器用于说明目的。
装饰者:
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
现在我想装饰我的class的所有方法。一种简单的方法是在我的 class 之上使用 @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
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()
我不确定
- 为什么“a) 覆盖
__new__
”不起作用? - 为什么方法
__new__
的签名在 metaclass 中不同?
有人可以帮助我了解我在这里缺少什么。
您似乎混淆了 __new__
和 metaclasses。调用 __new__
来创建一个新对象(来自 class 的实例,来自 metaclass 的 class),它不是 'class created' 钩子。
正常模式是:
Foo(...)
被翻译成type(Foo).__call__(Foo, ...)
,请参阅 special method lookups 了解原因。 class 的type()
是元 class.当
的实例,则对该实例调用Foo
是自定义的Python时使用的标准type.__call__
实现class将调用__new__
创建一个新实例, 如果结果确实是Foo
class:__init__
方法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
class 本身被创建时Foo.__new__
不会被调用,只有当Foo
的 实例 是已创建。
通常不需要在classes中使用__new__
,因为__init__
足以初始化实例的属性。但是对于 immutable 类型,比如 int
或 tuple
,你只能使用 __new__
来准备新的实例状态,因为你不能改变不可变对象创建后的属性。 __new__
在您想要更改 ClassObj()
生成的实例类型时也很有用(例如创建单例或生成专门的子 classes)。
相同的 __call__
-> __new__
并且可能 __init__
过程适用于 metaclasses。 class Foo: ...
语句是通过调用 metaclass 创建一个 class 对象来实现的,传入 3 个参数:class 名称,class 基数,以及class 正文,通常作为字典。使用 class Spam(metaclass = debugmeta): ...
,这意味着 debugmeta('Spam', (), {...})
被调用,这意味着 debugmeta.__new__(debugmeta, 'Spam', (), {...})
被调用。
您的第一次尝试 a,设置 Spam.__new__
无效,因为您 没有创建 class 对象 那里。相反,super().__new__(cls)
创建一个没有属性的空 Spam()
实例 ,因此 vars()
returns 一个空字典和 debugmethods()
最终什么都不做。
如果你想挂钩到 class 创建,那么你需要一个 metaclass.