2012-02-01 2 views
6

이것은 당황 스럽지만 확실한 답을 얻을 수 없습니다. DJango 모델에서 파생 된 클래스 내에서 __new__ 메서드 (또는보다 정확하게는 정적 메서드)를 사용합니다. 위의 클래스로 작동에서Django의 모델에서 파생 된 클래스에 __new__을 사용하면 작동하지 않습니다.

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") 

객체를 인스턴스화 :

이것은 __new__ 이상적 (우리가 장고를 사용하고 있기 때문에, 우리는 파이썬의 버전 2.x를 가정 할 수 있습니다 사용되는) 사용하는 방법입니다 예상했다. 이제, 하나의 장고 모델에서 파생 된 클래스에 이런 일을하려고 할 때, 예상치 못한 일이 발생합니다

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)은.

왜 이런 일이 일어나는 지 이해할 수 없습니다. (장고 프레임 워크로 인해 몇 가지 클래스 마술이 일어나고 있습니다.)

답변을 보내 주시면 감사하겠습니다.

+1

우선, 코드를 정리해야합니다. 마지막 스 니펫에서 '테스트'또는 '테스트'여야합니까? 또한 들여 쓰기가 꺼져 있습니다. – Marcin

+0

예, return super (test, self) ...는'return super (Test, sefl) ... '여야합니다. – Furbeenator

+0

안녕하세요 - 죄송합니다. 필자는 편집기에서 예제를 정말 빨리 타이핑했습니다. (필자는 코드를 복사하고 붙여 넣지 않았습니다.) - 잘못된 것에 대해 사과드립니다. 위의 두 가지 설명을 고려하더라도 여전히 작동하지 않습니다. BTW : 위의 코드 조각을 수정했습니다. –

답변

11

__new__은 첫 번째 매개 변수로 인스턴스를받지 못합니다. (a) 정적 메소드이고, (b) 인스턴스가 인스턴스를 생성하고 리턴하는 것이 어떨까요? __new__의 첫 번째 매개 변수는 클래스이므로 그대로 cls이라고합니다.

당신이 아주 이상한 오류 메시지를 만드는 이유는 무엇입니까? 일반적으로 매개 변수로 해당 클래스의 인스턴스 이외의 것으로 (즉, ClassName.methodName에 액세스하여 얻은 것)을 언 바운드 메서드로 호출했을 때 표시되는 오류 메시지입니다.

>>> 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__에서 볼 수는 언 바운드 방법 않을 것입니다 그러나, (__new__ 포함) staticmethods가되지 않는 언 바운드 방법, 그들은 클래스의 속성 될 일이 단순한 기능입니다. 게다가 (일반적인 방법과는 달리) 당신이 그것을 전달하는 것에있어 당신이 일관성이 있다는 것은 상관하지 않습니다; 실제로 Bar의 인스턴스를 생성하려면 과 Bar.__new__이 궁극적으로 같은 방식으로 구현되었으므로 (모든 실제 작업을 object.__new__으로 연기 함) Foo.__new__을 호출하여 구현할 수있었습니다.

그러나이 때문에 장고 자체의 소스 코드를 간략하게 살펴 보았습니다. Django의 Model 클래스는 메타 클래스가 ModelBase입니다. 어느 것이 매우 복잡하고, 전적으로 무엇을하고 있는지 알지 못했지만 매우 흥미로운 것을 발견했습니다.

ModelBase.__new__ 그것을 클래스 사전를 통과하지 않고 슈퍼 __new__를 호출합니다 (클래스 블록의 끝에서 클래스을 생성하는 기능입니다 메타 클래스__new__). 대신 __module__ 속성 집합 만있는 사전을 전달합니다.; add_to_class 대부분 단지 인 메타 클래스의 방법이다

# Add all attributes to the class. 
    for obj_name, obj in attrs.items(): 
     new_class.add_to_class(obj_name, obj) 

(attrs__new__ 기능을 포함하여 클래스 블록의 모든 정의를 포함하는 사전입니다 : 그런 다음, 처리의 전체 무리를하고 후에는 다음을 수행 setattr).

나는 __new__이 이상한 암시 적으로 정적 메서드이기 때문에 여기에 문제가 있음을 99 % 확신합니다. 따라서 다른 모든 정적 메서드와 달리 staticmethod 데코레이터를 적용하지 않았습니다. 파이썬 (어떤 수준에서)은 __new__ 메서드를 인식하고이를 정상적인 메서드가 아닌 정적 메서드로 처리합니다 [1]. 하지만 이것은 setattr을 사용하여 설정할 때가 아니라 클래스 블록에 __new__을 정의 할 때만 발생합니다.

정적 메서드 여야하지만 staticmethod 데코레이터로 처리되지 않은 __new__은 일반적인 인스턴스 메서드로 변환됩니다. 그런 다음 파이썬이 클래스Test을 호출하면 일반 인스턴스 작성 프로토콜에 따라 Test 인스턴스를 얻지 못한다고 불평합니다.

그 모든 올바른, 다음의 경우 장고가 조금 깨진 때문에

  1. 이 있지만, 실패 만이 __new__ 계정에 정적 것에 대해 파이썬의 불일치를 취할 실패한다.
  2. @staticmethod__new__ 방법에 적용하여이 작업을 수행 할 수도 있습니다.

[1] 나는 __new__staticmethod 장식하기 전에 도입 된 이후이, 파이썬의 역사적 특질이다 생각하지만, 그것을 호출 할 수있는 인스턴스가 없기 때문에 __new__는, 인스턴스를하지 수 .

+0

+1 멋지게 완료되었습니다. – philofinfinitejest

+0

우수 감사합니다. Ben. 정확히 찾고있는 답변입니다. 정적 장식자를 사용할 때 완벽하게 작동합니다. –

+0

'__new__'도 가능합니다. – jfs

관련 문제