代码:
import functools
class MyInt1(int):
def __new__(cls, x, value):
print("MyInt1.__new__", cls, x, value)
return super().__new__(cls, x, base=2)
def __init__(self, x, value):
print("MyInt1.__init__", self, x, value)
self.value = value
super().__init__()
class MyInt2:
def __init__(self, x, value):
print("MyInt2.__init__", self, x, value)
self.value = value
def decorator(class_):
class Wrapper(class_):
def __new__(cls, *args, **kwargs):
print("Wrapper.__new__", cls, args, kwargs)
obj = super().__new__(cls, *args, **kwargs)
...
return obj
def __init__(self, *args, **kwargs):
print("Wrapper.__init__", self, args, kwargs)
functools.update_wrapper(self, class_)
super().__init__(*args, **kwargs)
return Wrapper
c = decorator(MyInt1)("101", 42)
print(c, c.value)
c = decorator(MyInt2)("101", 42)
print(c, c.value)
输出:
Wrapper.__new__ <class '__main__.decorator.<locals>.Wrapper'> ('101', 42) {}
MyInt1.__new__ <class '__main__.decorator.<locals>.Wrapper'> 101 42
Wrapper.__init__ 5 ('101', 42) {}
MyInt1.__init__ 5 101 42
5 42
Wrapper.__new__ <class '__main__.decorator.<locals>.Wrapper'> ('101', 42) {}
Traceback (most recent call last):
File "tmp2.py", line 42, in <module>
c = decorator(MyInt2)("101", 42)
File "tmp2.py", line 28, in __new__
obj = super().__new__(cls, *args, **kwargs)
TypeError: object() takes no parameters
- 如果我真的不知道传递给装饰器的类应该如何处理?
- 为什么它不适用于常见模式?
- 为什么默认
__new__
不接受__init__
参数?
我发现的唯一方法是inspect.isbuiltin
检查super().__new__
和分支,但这很脏。
最佳答案
很难找到有关 Python 基类(对象)行为的详细信息,这些行为是为了实际实现 __init__
而实现的。创建新类时:
object
自己的__init__
和__new__
除了 self
之外,方法不接受任何参数和cls
。然而,如果它们是从子类(恰好是 Python 中定义的所有其他类)调用的,这些方法的每个方法都会检查子类是否定义了其中一个而不是另一个(即对象的 __init__
检查是否被实例化的类也定义了 __new__
或未定义)。
如果任一方法发现反向方法已被覆盖而其本身没有被覆盖,它只会吞掉任何额外的参数:因此用户的类 __init__
可以有参数,而不必担心相同的参数 - 将传递给 object.__new__
会导致错误,
所以,您遇到的问题是,在此检查期间,例如对象的 __new__
发现你的 wrapper 有 __init__
定义 - 因此它不会吞咽任何参数 - 和错误,因为有额外的参数。
如果您保留此模式,解决此问题的唯一方法是在装饰器中重新实现相同的逻辑:
def decorator(class_):
def has_method(cls, meth):
# (FIXME:the check bellow does not take in account other applications of this decorator)
return any(meth in ancestor.__dict__ for ancestor in cls.__mro__[:-1]):
def has_new(cls):
return has_method(cls, "__new__")
def has_init(cls):
return has_method(cls, "__init__")
class Wrapper(class_):
def __new__(cls, *args, **kwargs):
print("Wrapper.__new__", cls, args, kwargs)
if (args or kwargs) and not has_new(cls) and has_init(cls):
args, kwargs = (), {}
obj = super().__new__(cls, *args, **kwargs)
...
return obj
def __init__(self, *args, **kwargs):
print("Wrapper.__init__", self, args, kwargs)
functools.update_wrapper(self, class_)
cls = self.__class__
if (args or kwargs) and not has_init(cls) and has_new(cls):
args, kwargs = (), {}
super().__init__(*args, **kwargs)
return Wrapper
https://mail.python.org/pipermail/python-list/2016-March/704027.html是存在这种行为的暗示 - 我已经在官方文档中看到过它,但在某些地方我不记得是哪个。
关于python-3.x - 使用类包装器和 __new__ 装饰类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41956677/