__new__ 方法给出错误对象。__new__() 只需要一个参数(要实例化的类型)

Dan*_*ood 15 python magic-methods python-3.x

为什么下面的代码会出错?

class Foo:
    def __new__(cls, *args, **kwargs):
        print("Creating Instance")
        instance = super(Foo, cls).__new__(cls,*args, **kwargs)
        return instance

    def __init__(self, a, b):
        self.a = a
        self.b = b

z= Foo(2,3)
Run Code Online (Sandbox Code Playgroud)

它给出了以下错误

TypeError: object.__new__() takes exactly one argument (the type to instantiate)
Run Code Online (Sandbox Code Playgroud)

che*_*ner 19

    instance = super(Foo, cls).__new__(cls,*args, **kwargs)
Run Code Online (Sandbox Code Playgroud)

是正确的。但是,有责任首先删除您的类引入的参数,以便在object.__new__最终调用时,*args**kwargs都是空的。

你的代码应该是这样的

class Foo:
    def __new__(cls, a, b, *args, **kwargs):
        print("Creating Instance")
        instance = super(Foo, cls).__new__(cls, *args, **kwargs)
        return instance

    def __init__(self, a, b, *args, **kwargs):
            super().__init__(*args, **kwargs)
            self.a = a
            self.b = b
Run Code Online (Sandbox Code Playgroud)

该定义删除您的新的参数a,并bargs它传递到谁是对MRO未来之前。同样对于__init__.

  • 这是一个很好的建议,但我仍然无法理解为什么如果 __new__ 没有被覆盖,一切都会正常工作: __new__ 应该获取 cls 和构造函数参数。如果 __new__ 没有被重写,那么它就是 object.__new__,对吧?它获取所有这些附加参数(是吗?)并且从不抱怨,但是显式调用 super().__new__ 失败。 (4认同)

Sor*_*ary 9

除了这个这个答案之外,很高兴能在这里得到 Guido van Rossum 的回应。

object.__new__这解决了在子类中重写或不重写时的行为__new__以及传递该方法的额外参数会发生什么情况。

使用多个类参数进行调用是没有意义的 object.__new__(),任何这样做的代码都只是将这些参数转储到黑洞中。

忽略额外参数的唯一有意义的情况object.__new__()是当它没有被重写,但__init__ 正在被重写时——那么你就有了一个完全默认的值__new__,并且构造函数参数的检查被降级到__init__.

所有这一切的目的是捕获像 object(42) 这样的调用中的错误,该调用(再次)传递了未使用的参数。这通常是程序中存在错误的症状。

from inspect import signature

print(signature(object.__new__))
print('------------------------------')
print(signature(object.__init__))
print('------------------------------')
object(42)
Run Code Online (Sandbox Code Playgroud)

输出:

(*args, **kwargs)
------------------------------
(self, /, *args, **kwargs)
------------------------------
Traceback (most recent call last):
  File "<>", line 7, in <module>
    object(42)
TypeError: object() takes no arguments
Run Code Online (Sandbox Code Playgroud)

  • +1 这是实际的答案,我认为。接受的答案仅解释了如何避免应该捕获的错误。 (2认同)

Pet*_*cka 7

这应该有效:

class Foo:
    def __new__(cls, a, b):
        print("Creating Instance")
        instance = super(Foo, cls).__new__(cls)
        return instance


    def __init__(self, a, b):
        self.a = a
        self.b = b

foo_1 = Foo(a=1, b=2)
foo_2 = Foo(a=1, b=3)
Run Code Online (Sandbox Code Playgroud)

如果你想使用单例设计模式,并在构造函数中使用参数创建类的实例,这应该可以工作:

class FooSingleton:
    _instances = {}

    def __new__(cls, a, b):
        if cls not in cls._instances:
            print('creating new FooSingleton instance')
            cls._instances[cls] = super(FooSingleton, cls).__new__(cls)
        else:
            print('using FooSingleton instance')
        return cls._instances[cls]

    def __init__(self, a, b):
        self.a = a
        self.b = b

foo_s1 = FooSingleton(a=1, b=2)
foo_s2 = FooSingleton(a=1, b=3)
Run Code Online (Sandbox Code Playgroud)


小智 5

object.__new__()签名是(*args, **kwargs),您可以使用inspect.signature功能检查这一点。

但是为什么你有这个错误?TLDR:因为您定义了自定义__new__方法。

小研究

所有测试均在 Python 3.9.1 上完成。

考虑下一节课。

class MyClass:
    def __init__(self): pass
Run Code Online (Sandbox Code Playgroud)

让我们呼吁object.__new__()它:

>>> object.__new__(MyClass, *range(10), **{f'a{i}': i for i in range(10)})
<__main__.MyClass object at 0x000001E7B15B3550>
Run Code Online (Sandbox Code Playgroud)

完全没有问题。这个类只有 custom __init__,没有 custom __new__

现在尝试为您的 Foo 执行相同的调用:

>>> object.__new__(Foo, *range(10), **{f'a{i}': i for i in range(10)})
Traceback (most recent call last):
  File "<input>", line 1, in <module>
TypeError: object.__new__() takes exactly one argument (the type to instantiate)
Run Code Online (Sandbox Code Playgroud)

关于object.__new__(). 这个类有自定义__init____new__.

仅定义 custom 时,您将看到相同的错误__new__

>>> class OnlyNew:
...     def __new__(cls, *args, **kwargs): return super().__new__(cls)
>>> object.__new__(OnlyNew, *range(10), **{f'a{i}': i for i in range(10)})
Traceback (most recent call last):
  File "<input>", line 1, in <module>
TypeError: object.__new__() takes exactly one argument (the type to instantiate)
Run Code Online (Sandbox Code Playgroud)

让我们检查一个没有自定义__init____new__.

>>> class A: pass
>>> object.__new__(A, *range(10), **{f'a{i}': i for i in range(10)})
Traceback (most recent call last):
  File "<input>", line 1, in <module>
TypeError: A() takes no arguments
Run Code Online (Sandbox Code Playgroud)

完全不同的错误。

让我们来看看它是如何与继承一起工作的。从 A 派生并定义__init__

>>> class B(A):
...     def __init__(self): pass
>>> object.__new__(B, *range(10), **{f'a{i}': i for i in range(10)})
<__main__.B object at 0x000001E7B15D23A0>
Run Code Online (Sandbox Code Playgroud)

从 MyClass 派生并定义任何内容。

>>> class MC(MyClass): pass
>>> object.__new__(MC, *range(10), **{f'a{i}': i for i in range(10)})
<__main__.MC object at 0x000001E7B15D2CA0>
Run Code Online (Sandbox Code Playgroud)

从 MyClass 派生并定义__new__.

>>> class Sub(MyClass):
    def __new__(cls, *args, **kwargs): return super().__new__(cls)
>>> object.__new__(Sub, *range(10), **{f'a{i}': i for i in range(10)})
Traceback (most recent call last):
  File "<input>", line 1, in <module>
TypeError: object.__new__() takes exactly one argument (the type to instantiate)
Run Code Online (Sandbox Code Playgroud)

派生自 Foo 并且不定义任何内容。

>>> class F(Foo): pass
>>> object.__new__(F, *range(10), **{f'a{i}': i for i in range(10)})
Traceback (most recent call last):
  File "<input>", line 1, in <module>
TypeError: object.__new__() takes exactly one argument (the type to instantiate)
Run Code Online (Sandbox Code Playgroud)

现在让我们看一个绝对奇特的案例:

class Base:
    def __init__(self): pass
    def __new__(cls, *args, **kwargs): return super().__new__(cls)


class Sub(Base):
    def __init__(self): pass
    __new__ = object.__new__


class Free:
    def __init__(self): pass
    __new__ = object.__new__
Run Code Online (Sandbox Code Playgroud)
>>> object.__new__(Free, *range(10), **{f'a{i}': i for i in range(10)})
<__main__.Free object at 0x000001E7B15C5A90>
>>> object.__new__(Sub, *range(10), **{f'a{i}': i for i in range(10)})
Traceback (most recent call last):
  File "<input>", line 1, in <module>
TypeError: object.__new__() takes exactly one argument (the type to instantiate)
Run Code Online (Sandbox Code Playgroud)

Sub 和 Free 都没有自定义__new__方法 - 在两个类中__new__都是object.__new__(). 但是创建 Sub 会引发错误,而创建 Free 不会。似乎object.__new__()检查 not getattr(A_Class, '__new__', object.__new__) is object.__new__but all(getattr(cls, '__new__', object.__new__) is object.__new__ for cls in A_Class.mro())

结论

  1. 如果一个类__new__在其MRO 中object.__new__()使用 >1 个参数调用会引发 TypeError。
  2. 如果一个类只有自定义__init__并且__new__在其 MRO中没有自定义,则object.__new__()使用 >1 个参数调用会创建一个正确的实例。
  3. 如果一个类没有自定义都__init____new__其MRO,调用object.__new__()与> 1个参数引发类型错误。


小智 5

您无需担心将参数传递给超类。参数也将传递到调用中__init__

class User(object):
    def __new__(cls, *args, **kwargs):
        # do some logic with the initial parameters

        return super().__new__(cls)
Run Code Online (Sandbox Code Playgroud)