注意:使用Python
的flyweight实现的一部分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__
,和。{ 只接受一个参数,正在构造的类(它 在构造对象之前调用,因此没有自我 参数)。它还必须返回新创建的对象。
答案 0 :(得分:14)
以super()
开头本身只是super(A, B)
的简写,其中A
是代码出现的类,B
是函数的第一个参数其中出现代码;因此,在您的特定情况下,super().__new__(cls)
会扩展为super(CarModel, cls).__new__(cls)
。
反过来,super(T, O)
返回“超级对象”。要了解超级对象的作用,您需要了解实例和类的属性引用如何在Python中工作。
假设不涉及__getattr__
或__getattribute__
方法,请在对象A
上引用属性O
(即评估O.A
或{{1} })继续执行以下步骤:
getattr(O, "A")
的实例dict("A"
)中定义了O
,那么该dict上的值将直接返回,就像它一样。O.__dict__
方法解析顺序中的每个类,在每个单词中查找O
。如果找到,请调用值"A"
。D
反过来没有定义D
,那么它会按原样返回。但是,如果确实如此,那么__get__
被称为“描述符”,其D
方法被调用,__get__
作为第一个参数,O
作为第二个论点。类的属性引用大致相同,用实例引用的类替换,但有以下区别:
type(O)
方法时,__get__
作为第一个参数,而类被引用为第二个参数。Python使用描述符来实现诸如实例方法,类方法,静态方法和属性之类的东西。
然后,使用None
创建的超级对象是一个(内置)对象,其中包含super(T, O)
方法,并在其上的每个属性引用上调用,并在dicts中查找属性在__getattribute__
的MRO中,只有在T 之后的类。它随后找到的值,它会像往常一样调用O
。
这个过程有点复杂,举个例子,这就是它如何适用于你的特定情况。由于__get__
按其定义,因此其MRO为CarModel
。
[CarModel, object]
扩展为super().__new__(cls)
,如上所述。super(CarModel, cls).__new__(cls)
以产生超级对象super(CarModel, cls)
。S
上获取属性"__new__"
(相当于在Python代码中调用S
)。getattr(S, "__new__")
类创建S
以来,它会在CarModel
的MRO中考虑CarModel
后面的类,并在字典中找到CarModel
"__new__"
类本身。它的值是一个静态方法,它有一个object
方法,使用参数__get__
和None
调用。由于cls
是一个静态方法,因此它的__new__
方法只是原封不动地返回函数。因此,__get__
与super(CarModel, cls).__new__
完全相同。object.__new__
参数调用最后一步中获得的函数(即object.__new__
),其中cls
可能是cls
,最后是一个新实例CarModel
班。我希望这完全可以理解。
(为了完整起见,应该提到CarModel
类上的实际__new__
函数实际上不是静态方法,而是一个没有{的特殊内置函数{1}}方法,但由于静态方法上的object
方法只返回它们定义的函数,效果是相同的。)
答案 1 :(得分:4)
super()
用于引用超类(即从中进行子类化的父类)。
__new__
是一个被调用的方法,用于创建类的新实例(如果已定义)。
答案 2 :(得分:1)
我相信,你使用的是Python3,其中super不需要提供相同的类名。 Super指的是当前类的Base类,并为您从正确的基类调用方法做了正确的Method Resolution Order。 __new__
是调用以创建实例的方法。这是first step in the instance creation。
答案 3 :(得分:0)
我认为这个答案的一部分对 __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)
好吧,那没有帮助。按照@Dolda2000
的建议,让我们通过打印它们并使用*args
传递它们来检查那些 **kwargs
和 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
?
虽然这是真的,但需要指出的是:
object
只需要 cls
变量(实际上并不需要 *args/**kwargs
)__new__
的实现可以接受传递给您的 __init__
的任何值,并且对它们进行任何您想做的事情。__new__
返回一个未实例化的类,然后在其上调用 __init__
。有了这些理论,让我们再试一次,用 return super().__new__(cls, *args, **kwargs)
替换 return super().__new__(cls)
它现在按预期运行:
<块引用>PrintNew
(cls, *args, **kwargs)
(self, a)
new args: (42,) new kwargs: {}
42
这可能会给您一些关于如何实施的想法。