__new__
не получает экземпляр в качестве первого параметра.Как это могло случиться, когда (а) это статический метод, как вы заметили, и (б) его задача - создать экземпляр и вернуть его!Первый параметр __new__
условно называется cls
, так как это класс.
, что делает сообщение об ошибке, которое вы цитируете, очень странным;обычно это сообщение об ошибке, которое вы получаете, когда вызываете несвязанный метод (то есть то, что вы получаете, получая доступ к ClassName.methodName
) с чем-то отличным от экземпляра этого класса в качестве параметра 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__
в конечном итоге реализованы одинаково (откладывая всю фактическую работу до object.__new__
).
Однако этовкратце заставил меня взглянуть на исходный код самого Django.У класса Model
Джанго есть метакласс ModelBase
.Это довольно сложно, и я не понял, что он делает полностью, но я заметил кое-что очень интересное.
ModelBase.__new__
(метакласс __new__
, который являетсяфункция, которая создает class в конце вашего блока классов), вызывает его супер __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
это метод метакласса, который в основном просто setattr
).
Теперь я на 99% уверен, что проблема здесь, потому что __new__
- странный неявно статический метод.Таким образом, в отличие от любого другого статического метода, вы не применили к нему декоратор staticmethod
.Python (на некотором уровне) просто распознает метод __new__
и обрабатывает его как статический метод, а не как обычный метод [1].Но держу пари, что это происходит только тогда, когда вы определяете __new__
в блоке класса, а не когда вы устанавливаете его с помощью setattr
.
Так что ваш __new__
, который должен быть статическим методом, но не имеетt обработан декоратором staticmethod
, преобразуется в обычный метод экземпляра.Затем, когда Python вызывает его, передавая класс Test
, в соответствии с обычным протоколом создания экземпляра, он жалуется, что не получает экземпляр Test
.
Если все правильно,затем:
- Это терпит неудачу, потому что Django немного сломан, но только потому, что ему не удается учесть несогласованность Python по поводу статичности
__new__
. - Возможно, вы могли бы сделать этоработать, применяя
@staticmethod
к вашему __new__
методу, даже если вам не нужно это делать.
[1] Я считаю, что это историческая особенность Python, поскольку __new__
был представлен до staticmethod
декоратора, но __new__
не может взять экземпляр, так как нет экземпляра для его вызова.