Python 在从Django'派生的类上使用uuu new uuu;s模型不起作用

Python 在从Django'派生的类上使用uuu new uuu;s模型不起作用,python,django,django-models,Python,Django,Django Models,这件事让我感到困惑,但我无法得到一个明确的答案。在从DJango模型派生的类中使用\uuuu new\uuu方法(或者更准确地说,静态方法) 这就是理想情况下应该如何使用\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu(因为我们使用的是Django,所以我们可以假设使用的是python的2 实例化上述类中的对象的工作方式与预期相同。现在,当我们在从Django模型派生的类上尝试类似的方法时,会发生意想不到的事情: clas

这件事让我感到困惑,但我无法得到一个明确的答案。在从DJango模型派生的类中使用
\uuuu new\uuu
方法(或者更准确地说,静态方法)

这就是理想情况下应该如何使用
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
(因为我们使用的是Django,所以我们可以假设使用的是python的2

实例化上述类中的对象的工作方式与预期相同。现在,当我们在从Django模型派生的类上尝试类似的方法时,会发生意想不到的事情:

class Test(models.Model):
  def __new__(self, *args, **kwargs):
    return super(Test, self).__new__(self, *args, **kwargs)
实例化上述类中的对象会导致此错误:

TypeError:必须使用测试实例作为第一个参数调用未绑定的方法\uuuuu new\uuuuuu()(改为使用ModelBase实例)

我不明白为什么会发生这种情况(尽管我知道由于Django框架,一些类魔术正在幕后发生)


任何答案都将不胜感激。

\uuu new\uuu
不接收实例作为其第一个参数。当(a)它是一个静态方法,正如您所注意到的,(b)它的工作是创建一个实例并返回它时,它怎么可能呢!
\uuuu new\uuuu
的第一个参数通常称为
cls
,因为它是类

这使得您引用的错误消息非常奇怪;它通常是调用未绑定方法时得到的错误消息(即通过访问
ClassName.methodName
)而不是将该类的实例作为
self
参数。但是,staticmethods(包括
\uuuuu new\uuuu
)并不是未绑定的方法,它们只是碰巧是类属性的简单函数:

>>> 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>
attrs
是包含类块中所有定义的字典,包括
\uuuuu new\uuuuu
函数;
add\u to\u class
是一个元类方法,主要是
setattr

我现在99%确定问题在这里,因为
\uuuu new\uuu
是一种奇怪的隐式静态方法。因此,与其他静态方法不同,您没有对其应用
staticmethod
decorator。Python(在某种程度上)只识别
\uuuu new\uuu
方法,并将其作为静态方法处理,而不是普通方法[1]。但我敢打赌,只有在类块中定义
\uuuu new\uuuu
时才会发生这种情况,而不是在使用
setattr
进行设置时

因此,您的
\uuuuu new\uuuuu
,应该是一个静态方法,但尚未由
staticmethod
decorator处理,正在转换为一个普通实例方法。然后,当Python调用它通过
测试
时,按照正常的实例创建协议,它会抱怨没有得到
测试
的实例

如果这些都是正确的,那么:

  • 这失败了,因为Django有点不稳定,但只是因为它没有考虑Python关于
    \uuuu new\uuuu
    是静态的不一致性
  • 您可以通过将
    @staticmethod
    应用到您的
    \uuuuuuuuuuuu新的\uuuuuuuuuuu
    方法来实现这一点,即使您不必这样做


  • [1] 我相信这是Python的一个历史怪癖,因为
    \uuuuuuuuuuuuuuuuuuuuuu
    是在
    staticmethod
    decorator之前引入的,但
    \uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
    不能以一个实例为例,因为没有实例可以调用它。

    。在最后一段代码中,应该是
    test
    还是
    test
    ?另外,缩进已关闭。是的,
    return super(test,self).
    应该是
    return super(test,sefl).
    。大家好,对不起,我在编辑器中很快输入了示例(我没有在那里复制和粘贴代码)-因此,对于错误,我深表歉意。即使考虑到你上面的两条评论,它仍然不起作用。顺便说一句:我已经修复了上面的代码片段。
    models.Model.\uuuuu new\uuuuu(Test,*args,**kwargs)
    有效吗?不幸的是,我仍然收到相同的错误:(非常好,谢谢Ben。这正是我一直在寻找的答案。当我使用静态装饰器时,它现在工作得非常好。
    \uuuu new\uuuuu
    也可以是
    classmethod
    @J.F.Sebastian:从某种意义上说,它显然应该是一个classmethod,从逻辑上看。无论多么正常的
    \uu new\uu
    方法都是最确定的ly不是类方法,它们是静态方法。这也是一个历史怪癖,因为
    \uuuuu new\uuuuu
    早于
    类方法
    装饰器。鉴于
    \uuuuu new\uuuuuu
    已经是“特殊的”,并且是Python OO模型的核心所依赖的,我不会尝试在生产代码中使用类方法,尽管它可以我做得很好。@Ben:这是对“必须是静态的”的评论,也就是说,
    \uuuu new\uuuu
    是一种
    静态方法
    ,但也可以是
    类方法
    。BDFL说
    \uu new\uuuuuuuuu
    类方法
    出现的原因。@J.F.Sebastian啊!我用“静态”的意思只是“不作用于实例”在那句话里,你说得很对,让人困惑。我已经改写了。
    >>> 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>
    
        # Add all attributes to the class.
        for obj_name, obj in attrs.items():
            new_class.add_to_class(obj_name, obj)