13

假设我有一些调用 的类,__new__我如何使用 mro 并__new__在必要时调用超类(带参数),但不调用object.__new__附加参数?例如,这仅在您不向构造函数传递任何参数时才有效:

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

但是使用参数调用会导致它失败(因为在某些时候 Python 更改为object.__new__除了调用者类之外不接受任何参数):

>>> C(1)
A.__new__ called
B.__new__ called
Traceback (most recent call last):
   ...
TypeError: object() takes no parameters

那么怎么能AB说他们的超类是object?我应该做类似的事情super(A, cls).__new__ is object.__new__吗?检查那个mro()[1] == builtins.object?还是我只需要决定“A永远不会尝试很好地调用超类__new__”(例如通过做return object.__new__(cls))?

编辑:如果类定义了一个自定义__init__方法,Python 2.6 和 Python 2.7 可以使用它,但它仍然无法在 Python 3 中工作。

4

2 回答 2

10

如果我们查看CPython 源代码,我们会看到:

  • object.__new__如果使用额外的参数调用会出错,并且要么__new__被覆盖,要么未被__init__覆盖;同样地,
  • object.__init__如果使用额外的参数调用会出错,并且要么__init__被覆盖__new__要么未被覆盖

在 2.x 中,错误只是一个DeprecationWarning,您可能在试验时错过了它(请注意,因为默认情况下DeprecationWarning抑制2.7 和 3.2 )。

这种行为的理由是类型要么是不可变的——在这种情况下它们应该使用 in 中的参数__new__——要么是可变的——在这种情况下它们应该使用 in 中的参数__init__。这似乎是实用性胜过纯度的案例。很难想到委派给超类型的许多生产性用途(而不是仅仅记录/跟踪),而且使用额外参数的调用是错误__new__的情况将更加普遍。superobject.__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() takes no parameters");
        return NULL;
    }
    
  • object.__new__对参数不做任何事情,除了决定是否出错。

于 2013-11-01T10:53:49.203 回答
2

__new__不传递它自己使用和需要的参数实际上取决于自定义方法。如果两者都没有CB或者A不需要参数,那么object.__new__()引发异常的事实是完全正确的。

请注意,一旦您也有 custom ,那么__init__ object.__new__不会引发异常。那是因为现在有地方可以将剩余的参数交给

>>> 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):
...     def __init__(self, *args, **kwargs):
...         print args, kwargs
... 
>>> C(1)
A.__new__ called
B.__new__ called
(1,) {}
<__main__.C object at 0x10e7f9790>

看来这种行为在 Python 3 中发生了变化。

可以测试看看__new__返回的是什么方法:

class B(object):
    def __new__(cls, *args, **kwargs):
        new = super(B, cls).__new__
        if new is object.__new__:
            return new(cls)
        return new(cls, *args, **kwargs)
于 2013-10-31T23:08:48.673 回答