这令我感到困惑,但我无法得到明确的答案。在从DJango模型派生的类中使用__new__
方法(或更准确地说,静态方法)。
这是理想使用__new__
的方式(因为我们使用Django,我们可以假设正在使用python的2.x版本):
class A(object):
def __new__(self, *args, **kwargs):
print ("This is A's new function")
return super(A, self).__new__(self, *args, **kwargs)
def __init__(self):
print ("This is A's init function")
从上面的类实例化对象按预期工作。现在,当一个人在从Django模型派生的类上尝试这样的事情时,会出现意想不到的事情:
class Test(models.Model):
def __new__(self, *args, **kwargs):
return super(Test, self).__new__(self, *args, **kwargs)
从上述类中实例化对象会导致此错误:
TypeError: unbound method __new__() must be called with Test instance as first argument (got ModelBase instance instead)
。
我无法理解为什么会发生这种情况(虽然我知道由于Django框架,一些类魔法正在幕后发生)。
任何答案都将不胜感激。
答案 0 :(得分:11)
__new__
未收到实例作为其第一个参数。怎么可能当(a)它是一个静态方法,如你所注意到的,以及(b)它的工作是创建一个实例并返回它! __new__
的第一个参数通常称为cls
,因为它是类。
这使得您引用的错误消息非常奇怪;它通常是您在使用该类的实例以外的其他内容调用未绑定方法(即通过访问ClassName.methodName
获得的内容)时获得的错误消息{{1参数。但是,staticmethods(包括self
)不会成为未绑定的方法,它们只是简单的函数,恰好是类的属性:
__new__
您可以从中看到>>> class Foo(object):
def __new__(cls, *args, **kwargs):
return object.__new__(cls)
def method(self):
pass
>>> class Bar(object):
pass
>>> Foo.method
<unbound method Foo.method>
>>> Foo.__new__
<function __new__ at 0x0000000002DB1C88>
>>> Foo.method(Bar())
Traceback (most recent call last):
File "<pyshell#36>", line 1, in <module>
Foo.method(Bar())
TypeError: unbound method method() must be called with Foo instance as first argument (got Bar instance instead)
>>> Foo.__new__(Bar)
<__main__.Bar object at 0x0000000002DB4F28>
永远不应该是未绑定的方法。此外(与普通方法不同)它并不关心你通过它的方式是否一致;我实际上设法通过调用__new__
来构建Bar
的实例,因为它和Foo.__new__
最终都以相同的方式实现(将所有实际工作推迟到Bar.__new__
)。 / p>
然而,这让我简单地看一下Django本身的源代码。 Django的object.__new__
类有一个元类Model
。这是非常复杂的,我并没有弄清楚它在做什么,但我确实注意到了一些非常有趣的东西。
ModelBase
(元类 ModelBase.__new__
,这是在类块结束时创建类的函数)调用其超级__new__
未传递您的班级词典。它改为传递仅包含__new__
属性集的字典。然后,在完成一大堆处理之后,它执行以下操作:
__module__
( # Add all attributes to the class.
for obj_name, obj in attrs.items():
new_class.add_to_class(obj_name, obj)
是包含您的类块中所有定义的字典,包括attrs
函数; __new__
是元类方法,大部分只是add_to_class
)。
我现在99%确定问题就在这里,因为setattr
是一个奇怪的隐式静态方法。因此,与其他所有静态方法不同,您尚未将__new__
装饰器应用于它。 Python(在某种程度上)只识别staticmethod
方法并将其作为静态方法处理而不是普通方法[1]。但我敢打赌,这只会在您在类块中定义__new__
时发生,而不是在您使用__new__
进行设置时。
因此,您的setattr
(应该是静态方法但尚未由__new__
装饰器处理)正在转换为普通实例方法。然后当Python调用它传递类 staticmethod
时,按照正常的实例创建协议,它会抱怨它没有获得Test
的实例。
如果这一切都正确,那么:
Test
的静态不一致。__new__
应用于@staticmethod
方法来完成此工作,即使您不应该这样做。 [1]我认为这是Python的历史怪癖,因为__new__
是在__new__
装饰器之前引入的,但是staticmethod
不能采取一个实例,因为没有实例可以调用它。