2012-07-13 3 views
3

최근 메타 클래스에서 파생 클래스 메서드를 호출하는 중 문제가 발생했습니다.파이썬에서 classmethod를 파생시키는 올바른 방법은 무엇입니까?

class testMetaA(type): 
    def __init__(cls,cname,bases,cdict): 
     print "in testMetaA: %s"%cls 

그런 다음 나는 메타 클래스를 사용할 수 있습니다 예를 들어, 나는 실제로 인쇄 CLS를 제외한 아무것도하지 메타 클래스를 구축 do1(a)

class testA(object): 

    @classmethod 
    def do1(cls, a): 
     print "in testA:",cls, a 

그런 다음 classmethod를 가지고 간단한없는 baseclass testA를 얻을 예상대로 작동하는 하위 클래스 testB을 작성하십시오.

class testB(testA): 

    @classmethod 
    def do1(cls, a): 
     print "in testB: %s"%cls 
     super(testB, cls).do1(a) 
    __metaclass__=testMetaA 

인쇄 할 항목 : in testMetaA: <class '__main__.testB'>; 와 같은 testB.do1(a) 작품은 예상 :

>>> testB.do1('hello') 
in testB: <class '__main__.testB'> 
in testA: <class '__main__.testB'> hello 

그러나, 나는 testMetaB 다음으로 "super"를 포함하는 메타 클래스 내부의 classmethod를 호출하려고하면, 에러 올릴 것이다 : NameError: global name 'testC' is not defined합니다.

class testMetaB(type): 
    def __init__(cls,cname,bases,cdict): 
     print "in testMetaB: %s"%cls 
     cls.do1("hello") 

class testC(testA): 

    @classmethod 
    def do1(cls, a): 
     print "in testC: %s"%cls 
     super(testC, cls).do1(a) 
    __metaclass__=testMetaB 
내가 마지막으로 사용 super(cls, cls) 대신 super(testC, cls)에 의해 그것을 해결하는 방법을 찾을 수

:

class testD(testA): 

    @classmethod 
    def do1(cls, a): 
     print "in testD: %s"%cls 
     super(cls, cls).do1(a) 
    __metaclass__=testMetaB 

그것은 인쇄됩니다 같은 :

in testMetaB: <class '__main__.testD'> 
in testD: <class '__main__.testD'> 
in testA: <class '__main__.testD'> hello 

testD.do1(a)는 예상대로 작동합니다

>>> testD.do1('Well done') 
in testD: <class '__main__.testD'> 
in testA: <class '__main__.testD'> Well done 

이제 classmethod에서 super를 사용하는 가장 올바른 방법이 궁금합니다. 현재 클래스 이름을 명시 적으로 작성하는 대신 항상 super(cls,cls)을 사용해야합니까?

감사합니다.

@jsbueno

If some piece of code resorts to tricks like dynamically creating derived classes, that is important - one should not use the class name as first parametere to Super if that name is assigned to another object than the class itself. Instead, cls for class methods, or self.__class__ for instance methods can be passed to Super.

이는 일반적으로 슈퍼에 클래스 이름을 사용하는 나쁜 생각을 의미합니까?

나에게 일반적으로 정상적인 방법으로 super(type(self.__class__),self) 대신 super(type(self),self)을 사용합니다. self.__class__을 사용하는 데 큰 이점이 있는지 여부는 알 수 없습니다. @jsbueno 예제를 이와 같이 반복합니다. 여기서 C는 super(type(self),self)입니다. 따라서 D2()은 클래스 C이 변경되는 동안 동작을 변경하지 않습니다.

>>> class A(object): 
    def do(self): 
     print "in class A" 


>>> class B(A): 
    def do(self): 
     super(B, self).do() 


>>> class C(A): 
    def do(self): 
     super(type(self),self).do() 

>>> D1=B 
>>> D2=C 
>>> D1().do() 
in class A 
>>> D2().do() 
in class A 
>>> class B(A): 
    def do(self): 
     print "in new class B" 


>>> D1().do() 

Traceback (most recent call last): 
    File "<pyshell#52>", line 1, in <module> 
    D1().do() 
    File "<pyshell#37>", line 3, in do 
    super(B, self).do() 
TypeError: super(type, obj): obj must be an instance or subtype of type 
>>> class C(A): 
    def do(self): 
     print "in new class C" 
>>> D2().do() 
in class A 

는 @Don 질문의 제안에 따라, 나는 여기에 파이썬 버전을 넣어 : sys.version = 2.7.2+ (기본 10 월 4 2011 20시 6분 9초) [GCC 4.6.1]

+0

@Don 질문 : 여기

클래스 이름에 바인딩 전역 이름을 보여주는 조각이 슈퍼 무엇이입니다. "당신의 메타 클래스 testMetaA 실제로 클래스를 구축하지 않습니다" 그것은, 당신은 실제로'__init__'에서 함수를 감싸려고 할 수 있습니다. 오버라이드하지 않으면 메타 클래스'__new__ '는 슈퍼 메타 클래스의'__new__'을 사용하지 않는다는 것을 의미하지 않습니다. 온라인에서 많은 예제를 찾을 수 있습니다. 그리고 평범한 클래스에서와 같이, '__new__'의 메타 클래스에 대한 첫 번째 인수는'__init__'의 첫 번째 인수 인 metaclass와 완전히 다르다는 것을 알아야합니다. – Wang

+0

클래스 이름이나 기본 튜플을 수정하는 것을 제외하고 항상'__init__'을 고수 할 수 있습니다. '__new__' 이후의'__init__ '안에는 메타 클래스의 인스턴스 cls가 실제로 존재하기 때문에'__new__'보다 더 편리하다고 생각합니다. 왜냐하면'__new__ '안에서'cls = super (testMetaB, meta) .__ new __ (meta, cname, bases, cdict)'를 사용하여 cls를 먼저 만들 수 있습니다. 그런 다음'__new__'에서 수정하십시오. – Wang

+0

나에게'self'는 실제로 정상적인 클래스의 인스턴스를 참조합니다. 나는 보통 metaclass의'__new__'의 첫 번째 인자로'meta'를 사용하고'cls'는 메타 클래스의 인스턴스 (보통 클래스)를 참조합니다. 원하는 단어를 사용할 수 있습니다. 중요하지 않습니다. 어쨌든 내 질문에는 전혀 관련이 없습니다. – Wang

답변

3

However, if I try to call the classmethod inside the metaclass which contains a "super" as following testMetaB, it will raise an error: NameError: global name 'testC' is not defined.

이름 TestC은 메타 클래스는이 작업의 완료 후 새로운 클래스에 바인드됩니다 - 그것은 __init__ (그리고 __init__ 전에 __new__) 방법의에서 그 반환 후입니다.

"super"호출을 사용하면 클래스 이름이 첫 번째 매개 변수로 사용되므로 클래스 이름이 마술처럼 표시되지 않습니다. 클래스 자체가 할당되는 전역 변수입니다 (정상 회람에서는) .

이 경우 이름은 아직 할당되지 않았지만 클래스 메서드이므로 cls 클래스의 참조가 있습니다. 그 이유는 그것이 작동하는 이유입니다.

코드의 일부가 파생 클래스를 동적으로 생성하는 등의 트릭을 사용하면 중요합니다. 클래스 자체가 클래스 자체가 아닌 다른 객체에 할당 된 경우 클래스 이름을 Super의 첫 번째 매개 변수로 사용하지 않아야합니다. 대신, 클래스 메소드의 경우 cls, 인스턴스 메소드의 경우 self.__class__을 Super로 전달할 수 있습니다.

>>> class A(object): 
... def do(self): 
...  print "In class A" 
... 
>>> class B(A): 
... def do(self): 
...  super(B, self).do() 
... 
>>> C = B 
>>> C().do() 
In class A 
>>> class B(object): 
... def do(self): 
...  print "in new class B" 
... 
>>> C().do() 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "<stdin>", line 3, in do 
TypeError: super(type, obj): obj must be an instance or subtype of type 
>>> 
+0

중요한 부분에서 일어나는 일을 좀 더 지적 할 수 있다면 좋을 것이다.내가 올바르게 이해한다면 두 번째 클래스 B가 C()를 통해 어떻게 호출되지 않는지를 보여 주려고 했습니까? –

+0

감사! 정상적인 방법으로, 나는 보통'super (type (self), self) .do()'와 같은 방식으로,'self .__ class__'보다 더 좋을 것 같아요? – Wang

+0

Wang : 실제로 - 유형 (자기)이'self .__ class__'보다 낫지 만 둘 다 동등합니다. – jsbueno