为什么 object.__new__ 在这三种情况下的工作方式不同 [英] Why does object.__new__ work differently in these three cases

查看:26
本文介绍了为什么 object.__new__ 在这三种情况下的工作方式不同的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

来自问题 为什么 object.__new__ 在这两种情况下的工作方式不同,或者更确切地说是如何工作的

作者对为什么不感兴趣,而对如何做感兴趣.

我很想知道为什么,特别是:

  1. 为什么 object.__init__ 不打印任何参数而不是 object.__new__(在 testclass1 中)

  2. 为什么 testclass3 没有出现错误?(因为它不需要除 self 以外的任何参数)

代码

<预><代码>>>>类 testclass1(对象):... 经过...>>>类 testclass2(对象):... def __init__(self,param):... 经过...>>>a = object.__new__(testclass1, 56)回溯(最近一次调用最后一次):文件<stdin>",第 1 行,在 <module> 中类型错误:object.__new__() 不带参数>>>b = object.__new__(testclass2, 56)>>>乙<__main__.testclass2 对象在 0x276a5d0>>>>类 testclass3(对象):... def __init__(self):... 经过...>>>c = object.__new__(testclass3, 56)>>>C<__main__.testclass3 对象在 0x276a790>>>>c1 = object.__new__(testclass3)>>>c1<__main__.testclass3 对象在 0x276a810>

解决方案

您使用的是较旧的 Python 版本;此错误消息已更新:

<预><代码>>>>object.__new__(testclass1, 56)回溯(最近一次调用最后一次):文件<stdin>",第 1 行,在 <module> 中类型错误:object() 不带参数

Python 只会抱怨 __init__ 不支持参数,如果 __new____init__ 都没有被覆盖;例如当您从 object 继承两者时.testclass1 适合这种情况,testclass3 不适合,因为它有一个 __init__ 方法.

这是为了支持实现不用于 __init__ 的不可变类型(在这种情况下将从 object 继承), 可变类型,其中 __new__ 不应该关心 __init__ 期望什么参数(通常是 more 参数).

请参阅 issue 1683368,其中 Guido van Rossum 解释了他的动机.

typeobject.c 源代码有话要说:

<块引用>

你可能想知道为什么 object.__new__() 只抱怨参数
object.__init__() 未被覆盖时,反之亦然.

考虑用例:

  1. 当两者都没有被覆盖时,我们希望听到关于多余的(即任何)参数,因为它们的存在可能表示存在错误.

  2. 当定义一个不可变类型时,我们很可能只覆盖__new__(),因为 __init__() 被调用太晚而无法初始化不可变对象.由于 __new__() 定义了类型,不得不重写 __init__() 只是为了阻止它抱怨过多的论据.

  3. 在定义可变类型时,我们很可能只覆盖__init__().所以在这里逆向推理适用:我们不想要覆盖 __new__() 只是为了阻止它抱怨.

  4. __init__() 被覆盖时,子类 __init__() 调用object.__init__(),后者应该抱怨过度论据;__new__() 同上.

用例 2 和 3 使得无条件检查多余的论据.解决所有四种用途的最佳解决方案情况如下: __init__() 抱怨过多的参数除非 __new__() 被覆盖并且 __init__() 未被覆盖(IOW,如果 __init__() 被覆盖或 __new__() 未被覆盖);对称地,__new__() 抱怨过多的参数,除非__init__() 被覆盖并且 __new__() 不被覆盖(IOW,如果 __new__() 被覆盖或 __init__() 未被覆盖).

然而,为了向后兼容,这破坏了太多代码.因此,在 2.6 中,我们将警告关于多余的参数,当两者方法被覆盖;对于所有其他情况,我们将使用上述规则.

请注意,.__init__() 方法本身 仍然会报错!创建实例时,__new____init__ 都会被调用;您的代码仅直接调用 __new__调用 __init__!如果传入参数,则创建 testclass1testclass3 的实例都会失败:

<预><代码>>>>测试类1(56)回溯(最近一次调用最后一次):文件<stdin>",第 1 行,在 <module> 中类型错误:object() 不带参数>>>testclass3(56)回溯(最近一次调用最后一次):文件<stdin>",第 1 行,在 <module> 中类型错误:__init__() 只需要 1 个参数(给定 2 个)

唯一的区别是对于 testclass1,它是 object() 的默认方法,而不是抱怨自定义 __init__ 的特定错误.

from question Why does or rather how does object.__new__ work differently in these two cases

the author wasn't interested in the why, but rather in the how.

I would very much want to understand why, particularly :

  1. why isn't object.__init__ takes no parameters printed instead of object.__new__ (in testclass1)

  2. why no error is raised for testclass3 ? (as it takes no arguments other than self)

code

>>> class testclass1(object):
    ...     pass
    ... 

>>> class testclass2(object):
    ...     def __init__(self,param):
    ...             pass
    ... 

>>> a = object.__new__(testclass1, 56)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: object.__new__() takes no parameters

>>> b = object.__new__(testclass2, 56)

>>> b
    <__main__.testclass2 object at 0x276a5d0>

>>> class testclass3(object):
    ...     def __init__(self):
    ...             pass
    ... 

>>> c = object.__new__(testclass3, 56)

>>> c
    <__main__.testclass3 object at 0x276a790>

>>> c1 = object.__new__(testclass3)

>>> c1
    <__main__.testclass3 object at 0x276a810>

解决方案

You are using an older Python version; the error message has since been updated:

>>> object.__new__(testclass1, 56)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: object() takes no parameters

Python will only complain about __init__ not supporting arguments if neither __new__ nor __init__ have been overridden; e.g. when you inherit both from object. testclass1 fits that case, testclass3 does not because it has an __init__ method.

This is to support implementing immutable types that don't have a use for __init__ (which would be inherited from object in that case), and mutable types, where __new__ should not care about what arguments __init__ expects (which usually would be more arguments).

See issue 1683368 where Guido van Rossum explains his motivations for this.

The typeobject.c source code has this to say:

You may wonder why object.__new__() only complains about arguments
when object.__init__() is not overridden, and vice versa.

Consider the use cases:

  1. When neither is overridden, we want to hear complaints about excess (i.e., any) arguments, since their presence could indicate there's a bug.

  2. When defining an Immutable type, we are likely to override only __new__(), since __init__() is called too late to initialize an Immutable object. Since __new__() defines the signature for the type, it would be a pain to have to override __init__() just to stop it from complaining about excess arguments.

  3. When defining a Mutable type, we are likely to override only __init__(). So here the converse reasoning applies: we don't want to have to override __new__() just to stop it from complaining.

  4. When __init__() is overridden, and the subclass __init__() calls object.__init__(), the latter should complain about excess arguments; ditto for __new__().

Use cases 2 and 3 make it unattractive to unconditionally check for excess arguments. The best solution that addresses all four use cases is as follows: __init__() complains about excess arguments unless __new__() is overridden and __init__() is not overridden (IOW, if __init__() is overridden or __new__() is not overridden); symmetrically, __new__() complains about excess arguments unless __init__() is overridden and __new__() is not overridden (IOW, if __new__() is overridden or __init__() is not overridden).

However, for backwards compatibility, this breaks too much code. Therefore, in 2.6, we'll warn about excess arguments when both methods are overridden; for all other cases we'll use the above rules.

Note that the .__init__() method itself will still complain! When you create an instance, both __new__ and __init__ are called; your code only calls __new__ directly and does not invoke __init__! Creating an instance of testclass1 and testclass3 both fails if you pass in arguments:

>>> testclass1(56)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: object() takes no parameters
>>> testclass3(56)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: __init__() takes exactly 1 argument (2 given)

The only difference is that for testclass1 it is the default methods for object() that complain instead a specific error for the custom __init__.

这篇关于为什么 object.__new__ 在这三种情况下的工作方式不同的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆