问题描述
假设我有一个调用 __new__
的类,我如何与 mro 配合使用并根据需要调用超类的 __new__
(带参数),但不调用带有附加参数的 object.__new__
?例如.这仅在您不向构造函数传递参数时才有效:
A 类(对象):def __new__(cls, *args, **kwargs):打印(A.__new__ 被调用")return super(A, cls).__new__(cls, *args, **kwargs)B类(对象):def __new__(cls, *args, **kwargs):打印(B.__new__ 调用")返回 super(B, cls).__new__(cls, *args, **kwargs)C(A,B)类:通过>>>C.mro()[__main__.C, __main__.A, __main__.B, builtins.object]>>>C()A.__new__ 被称为B.__new__ 被称为
但是使用参数调用会导致它失败(因为在某些时候 Python 更改为让 object.__new__
除了调用者类之外不接受任何参数):
如果该类定义了一个自定义的 __init__
方法,Python 2.6 和 Python 2.7 可以使用它,但它仍然不能在 Python 3 中工作.>
如果我们看一下 CPython源码,我们看到:
object.__new__
如果使用额外参数调用将出错,并且__new__
被覆盖或__init__
是 not 被覆盖;同样,object.__init__
如果使用额外参数调用将出错,并且__init__
被覆盖或__new__
是 not 覆盖
在 2.x 中,错误只是一个 DeprecationWarning
,您可能在试验时错过了它(请注意,自 2.7 和 3.2 DeprecationWarning
是 默认禁止).>
这种行为的理由是类型要么是不可变的——在这种情况下它们应该使用 __new__
中的参数——或者可变的——在这种情况下它们应该使用 __init__
中的参数>.这似乎是一个实用性胜过纯度的案例;很难想象 __new__
的许多生产性用途委托给超类型(而不是仅仅记录/跟踪),而且 code>super 调用带有额外参数的 object.__new__
是一个错误.
处理这个问题最简洁的方法是检查身份:super(A, cls).__new__ is object.__new__
.理由:
这是 CPython 决定是否出错的方式(在 C 而不是 Python 中,但逻辑是相同的):
if (excess_args(args, kwds) &&(type->tp_init == object_init || type->tp_new != object_new)) {PyErr_SetString(PyExc_TypeError, "object() 不带参数");返回空;}
object.__new__
对参数不做任何处理,除了决定是否出错.
Let's say I have some class that calls __new__
, how do I play well with the mro and call up to super classes' __new__
(with arguments) as necessary, but not call object.__new__
with additional arguments? E.g. this only works if you pass no arguments to the constructor:
class A(object):
def __new__(cls, *args, **kwargs):
print("A.__new__ called")
return super(A, cls).__new__(cls, *args, **kwargs)
class B(object):
def __new__(cls, *args, **kwargs):
print("B.__new__ called")
return super(B, cls).__new__(cls, *args, **kwargs)
class C(A, B): pass
>>> C.mro()
[__main__.C, __main__.A, __main__.B, builtins.object]
>>> C()
A.__new__ called
B.__new__ called
But calling with arguments causes it to fail (because at some point Python changed to have object.__new__
accept no parameters except for caller class):
>>> C(1)
A.__new__ called
B.__new__ called
Traceback (most recent call last):
...
TypeError: object() takes no parameters
So how can A
or B
tell that their super class is object
? Should I do something like super(A, cls).__new__ is object.__new__
? Check that mro()[1] == builtins.object
? Or do I just need to decide "A will never try to call super classes nicely in __new__
" (e.g. by doing return object.__new__(cls)
)?
EDIT: if the class defines a custom __init__
method, Python 2.6 and Python 2.7 are fine with it, but it will still not work in Python 3.
If we look at the CPython source, we see that:
object.__new__
will error if called with extra arguments, and either__new__
is overridden or__init__
is not overridden; likewise,object.__init__
will error if called with extra arguments, and either__init__
is overridden or__new__
is not overridden
In 2.x the error was just a DeprecationWarning
, which it's possible you may have missed when experimenting (note that since 2.7 and 3.2 DeprecationWarning
is suppressed by default).
The justification for this behaviour is that types are either immutable - in which case they should consume arguments in __new__
- or mutable - in which case they should consume arguments in __init__
. This appears to be a case of practicality beats purity; it's difficult to think of many productive uses of __new__
delegating to the supertype (as opposed to just logging/tracing), and it's going to be a lot more common that a super
call to object.__new__
with extra arguments is a mistake.
The cleanest way to handle this is going to be to check identity: super(A, cls).__new__ is object.__new__
. Justifications:
this is how CPython decides whether to error (in C rather than Python, but the logic is the same):
if (excess_args(args, kwds) && (type->tp_init == object_init || type->tp_new != object_new)) { PyErr_SetString(PyExc_TypeError, "object() takes no parameters"); return NULL; }
object.__new__
does nothing with the arguments, except to decide whether to error out.
这篇关于确定 super().__new__ 是否将是 Python 3 中的 object.__new__?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!