使用object.__new__在__new__中的缺点是什么?

8
编写异常类时,我遇到了这个错误:
TypeError: object.__new__(A) is not safe, use Exception.__new__()
在这里有一个类似的问题:TypeError: object.__new__(int) is not safe, use int.__new__()。所以__new__被弃用的原因如下:
[Python-Dev] __new__ deprecation

Guido van Rossum

"这个消息的意思就是它所说的。:-)调用object.__new__()没有多余的参数,任何这样做的代码都只是把那些参数扔进了一个黑洞中。"

但是,在3.3版本中我收到的警告“is not safe”让人感到不安。我试图理解使用object.__new__的影响,让我们考虑以下示例:
>>> class A(Exception):
...     def __new__(cls, *args):
...             return object.__new__(A)
...
>>> A()
TypeError: object.__new__(A) is not safe, use Exception.__new__()

彻底失败。另一个例子:
>>> class A(object):
...     def __new__(cls, *args):
...             return object.__new__(A)
...
>>>
>>> A()
<__main__.A object at 0x0000000002F2E278>

工作正常。虽然objectException一样是内置类,但它们都具有内置类的特点。现在,使用Exception,第一个示例会引发TypeError错误,但是使用object则不会?

(a) 使用object.__new__的缺点是什么,导致Python在第一个示例中引发错误(TypeError:...is not safe...)?

(b) Python在调用__new__之前执行什么样的检查?或者说:在第一个示例中,Python引发错误的条件是什么?

2个回答

10
调用object.__new__没有问题,但是不调用Exception.__new__会有问题。 Exception类的设计使得其关键在于必须调用其__new__方法,否则会报错。
为什么只有内置类才会出现这种情况呢?实际上,Python会对每个编程时需要这样做的类都进行相同的处理。
以下是一个简化版的自定义类中使用相同机制的示例:
class A(object):
    def __new__(cls):
        rtn = object.__new__(cls)
        rtn.new_called = True
        return rtn

    def __init__(self):
        assert getattr(self,'new_called',False), \
            "object.__new__ is unsafe, use A.__new__"

class B(A):
    def __new__(cls):
        return object.__new__(cls)

现在:

>>> A()
<__main__.A object at 0x00000000025CFF98>

>>> B()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 7, in __init__
AssertionError: object.__new__ is unsafe, use A.__new__

作为一个旁注,这个问题中的例子实际上有两个错误:
>>> class A(Exception):
...     def __new__(cls, *args):
...             return object.__new__(A)

第一个问题是__new__object上调用,因此忽略了Exception.__new__
另一个同样严重的问题是将A传递给__new__而不是cls,这会妨碍所有继承自A的类。
看下面的例子:
class A(object):
    def __new__(cls):
        return object.__new__(A)  # The same erroneous line, without Exception

class B(A):
    pass

现在B()不再创建B的实例:

>>> B()
<__main__.A object at 0x00000000025D30B8>

我刚意识到机制是在object.__new__中编码的。看看这段代码。也就是说,object.__new__会引发TypeError异常。现在说“不安全”非常合理。尽管如此,在子类中覆盖Exception.__new__不会引发错误,即使在Subclass.__new__内部没有调用Exception.__new__,这同样是危险的。 - GIZ

0
调用object.__new__(A)会返回一个A的实例,但如果定义了Exception.__new__(),则不会调用它。

但是如果定义了Exception.__new__(),则不会调用它。这不是问题,因为我们在子类中定义了__new__。这与是否调用Exception.__new__无关。如果我们有两个超类,每个超类都有__new__方法:C(A,B),那么将调用最左边的__new__,也就是A.__new__,而不会调用B.__new__。你能举个例子吗? - GIZ
你提出的是一个单独的问题,可以通过显式调用A.__new__()B.__new__()来解决,或者使用super并确保所有祖先类也使用它。如果未调用Exception.__new__,则会出现大问题,如果您错过了Exception特定的配置,并计划将您的对象传递给期望正确配置的Exception实例的人。 - chepner
我理解你的观点:(将__new__替换为另一个__new__可能会跳过对象创建期间所需的一些或全部关键配置)。 - GIZ
如果您考虑这段代码,您会注意到Python仅对内置类引发(TypeError:... is not safe...)异常。从代码中可以看出,类A有自己的__new__,类B直接继承自A并在其__new__中使用object.__new__(*b)。在Python 3.3中运行时不会引发任何错误。为什么Python仅对内置类引发异常,而不是用户定义的类呢? - GIZ

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接