【问题标题】:What does `super()` mean in `__new__``__new__` 中的 `super()` 是什么意思
【发布时间】:2012-01-29 21:26:28
【问题描述】:

注意:使用 Python 实现享元的一部分

import weakref

class CarModel:
    _models = weakref.WeakValueDictionary()

    def __new__(cls, model_name, *args, **kwargs):
        model = cls._models.get(model_name)
        if not model:
            model = super().__new__(cls)
            cls._models[model_name] = model    
        return model

    def __init__(self, model_name, air=False):
        if not hasattr(self, "initted"):
          self.model_name = model_name
          self.air = air
          self.initted=True

问题 1>super() 是什么意思?是指CarModel的父类吗?

问题 2> 我也很难理解函数__new__ 的工作原理?具体来说就是下面这行。

model = super().__new__(cls)

__new__的描述:

构造函数称为__new__,而不是__init__,并且 只接受一个参数,即正在构造的类(它 在构造对象之前调用,所以没有self 争论)。它还必须返回新创建的对象。

【问题讨论】:

  • @Ben,基于document__new__() 返回cls 的实例。为什么OP中的代码model = super().__new__(cls) 可以创建cls的实例? CarModel 的超类如何知道如何创建其子类的实例?
  • 这本身就是一个很好的 SO 问题(如果还没有被问到的话)。简短的回答是您将使用cls 参数传递给它当前类。最终,如果不一直推迟到object.__new__,就根本无法实际创建一个新实例,因为 Python 没有“分配实例”的指令。但是object.__new__ 知道创建它所传递的类的实例(这也将是object 的实例,因为一切都是object 的实例)。

标签: python python-3.x


【解决方案1】:

super() 开头本身就是super(A, B) 的简写,其中A 是代码所在的类,B 是代码所在函数的第一个参数;因此,在您的特定情况下,super().__new__(cls) 扩展为 super(CarModel, cls).__new__(cls)

反过来,super(T, O) 返回一个“超级对象”。要了解超级对象的作用,您需要了解实例和类上的属性引用在 Python 中是如何工作的。

假设不涉及__getattr____getattribute__ 方法,则在对象O 上引用属性A(即评估O.Agetattr(O, "A"))将执行以下步骤:

  1. 如果在O 的实例字典(O.__dict__) 中定义了"A",则直接返回该字典上的值,原样返回。
  2. 否则,依次检查O 的方法解析顺序中的每个类,在它们的每个dicts 中查找"A"。如果找到,请调用值D
  3. 如果D 反过来没有定义__get__,则返回原样。但是,如果确实如此,则 D 被称为“描述符”,并以 O 作为第一个参数,type(O) 作为第二个参数调用其 __get__ 方法。

类上的属性引用的工作方式大致相同,只是将引用的类替换为实例,但有以下区别:

  • 第 1 步不适用。
  • __get__ 方法被调用,None 作为第一个参数,被引用的类作为第二个参数。

Python 使用描述符来实现实例方法、类方法、静态方法和属性等。

使用super(T, O) 创建的超级对象是一个(内置)对象,带有__getattribute__ 方法,在其上的每个属性引用上都会调用该方法,并在 仅在 O 的 MRO 中跟随 T 的课程。然后它找到的值,它照常调用__get__

这个过程有点复杂,因此,作为一个例子,下面是它在您的特定情况下的工作方式。由于CarModel按原样定义,其MRO为[CarModel, object]

  1. super().__new__(cls) 扩展为 super(CarModel, cls).__new__(cls),如上所述。
  2. super(CarModel, cls) 被评估以产生一个超级对象 S
  3. Python 获取 S 上的属性 "__new__"(相当于在 Python 代码中调用 getattr(S, "__new__"))。
  4. 由于S 是在CarModel 类上创建的,它会考虑CarModel 的MRO 中CarModel 之后的类,并在object 类本身的字典中找到"__new__"。它的值是一个静态方法,有一个__get__ 方法,使用参数Nonecls 调用。由于__new__ 是一个静态方法,它的__get__ 方法只是简单地返回原样的函数,未经修改。因此,super(CarModel, cls).__new__object.__new__ 完全相同。
  5. 上一步得到的函数(即object.__new__)用cls参数调用,其中cls可能是CarModel,最后是CarModel类的新实例。李>

我希望这完全可以理解。

(为了完整起见,需要指出的是,object 类上的实际__new__ 函数实际上并不是静态方法,而是一个特殊的内置函数,它根本没有__get__ 方法全部,但是由于静态方法上的__get__ 方法只是返回它们定义的函数,所以效果是一样的。)

【讨论】:

  • super(class1, class2)中,super如何使用class1class2
  • @Adrian:我不认为我理解你的问题,因为这听起来像是整个答案的意思。有什么特别之处吗?
【解决方案2】:

super() 用于引用超类(即您要从其子类化的父类)。

__new__ 是一个被调用以创建类的新实例(如果已定义)的方法。

【讨论】:

  • super(class1, class2)中,super如何使用class1class2
【解决方案3】:

我相信,您使用的是 Python3,不需要为 super 提供相同的类名。 Super 引用当前类的基类,并为您执行正确的Method Resolution Order 以从正确的基类调用方法。 __new__ 是用于创建实例的方法。是first step in the instance creation

【讨论】:

  • super(class1, class2)中,super如何使用class1class2
【解决方案4】:

我认为这个答案的一部分对__new____init__ 之间的关系感到困惑。我自己对此感到困惑。以下是回答此类问题的方法。

在我们开始之前,重要的是要注意定义__new__ 总是有一个不可见/神奇的@classmethod 装饰器应用于它。

from inspect import signature # find function signature

class JustInit(object):
  def __init__(self, a):
    self.a = a

print("JustInit")
print(signature(JustInit.__new__))
print(signature(JustInit.__init__))
JustInit
(*args, **kwargs)
(self, a, b)

嗯,这没有帮助。让我们检查那些*args**kwargs,按照@Dolda2000 的建议打印它们并使用super 传递它们

class PrintNew(object):
  def __new__(cls, *args, **kwargs):
    print("new args:", args, "new kwargs:", kwargs)
    return super().__new__(cls, *args, **kwargs)

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

print("PrintNew")
print(signature(PrintNew.__new__))
print(signature(PrintNew.__init__))

pnew = PrintNew(42)
print(pnew.a)

结果:

PrintNew
(cls, *args, **kwargs)
(self, a)
new args: (42,) new kwargs: {}
Traceback (most recent call last):
  File "/home/rett/notes/python/super_new.py", line 21, in <module>
    pnew = PrintNew(42)
  File "/home/rett/notes/python/super_new.py", line 12, in __new__
    return super().__new__(cls, *args, **kwargs)
TypeError: object.__new__() takes exactly one argument (the type to instantiate)

等等...我以为new的签名是*args, **kwargs...哦,*args must really be just the cls

虽然这是真的,但非常重要的是要指出:

  1. object 只接受 cls 变量(实际上并不接受 *args/**kwargs
  2. __new__ 的实现可以采用传递给 __init__ 的任何值并对它们做任何你想做的事情
  3. 似乎__new__ 返回了一个未实例化的类,然后调用了__init__

有了这些理论,让我们再试一次,把return super().__new__(cls, *args, **kwargs)换成return super().__new__(cls)

它现在按预期运行:

PrintNew
(cls, *args, **kwargs)
(self, a)
new args: (42,) new kwargs: {}
42

这可能会给您一些关于如何进行实施的想法。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-25
    • 2010-09-28
    • 1970-01-01
    相关资源
    最近更新 更多