本文介绍了确定 super().__new__ 是否将是 Python 3 中的 object.__new__?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我有一个调用 __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__?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-11 07:28