2017-12-04 2 views
1

내가 먼저 인스턴스가 생성 될 때 클래스 변수를 초기화 메타 클래스를 구현하기 위해 노력하고 내부 호출하지 않습니다. 클래스 메서드 (예 : __new__)로 호출되어야하는 새로운 마법 메서드 __load__을 유지하고 싶습니다. 그래서 이런 식으로 구현 : 그것은 잘 작동하고 정확한 결과를 제공'classmethod'객체는 파이썬 3에서 메타 클래스의 방법

class StaticLoad(type): 
    __loaded_classes = set() 

    def __call__(cls, *args, **kwargs): 
     if cls not in cls.__loaded_classes: 
      if hasattr(cls, '__load__'): 
       cls.__load__() 
      cls.__loaded_classes.add(cls) 
     return super().__call__(*args, **kwargs) 


class BaseClass(metaclass=StaticLoad): 
    s = 0 


class MyClass(BaseClass): 
    @classmethod 
    def __load__(cls): 
     print("Loading", cls.__name__, "...") 
     cls.s += 1 


obj1 = MyClass() 
obj2 = MyClass() 
print(MyClass.s) 

: 할 필요없이 (

Loading MyClass ... 
1 

가 지금은 __new__ 같은 기본적으로 classmethod 같은 방법 __load__을 구현하려는를 매번 위에 @classmethod을 입력하십시오.

Traceback (most recent call last): 
    File "example.py", line 22, in <module> 
    obj1 = MyClass() 
    File "example.py", line 7, in __call__ 
    classmethod(cls.__load__)() 
TypeError: 'classmethod' object is not callable 

classmethod 루틴은 클래스 정의 내에서 제대로 볼 수 있습니다처럼 보이는 : 나는 오류가 발생했습니다

class StaticLoad(type): 
    __loaded_classes = set() 

    def __call__(cls, *args, **kwargs): 
     if cls not in cls.__loaded_classes: 
      if hasattr(cls, '__load__'): 
       # I try to apply classmethod routine to make 
       # cls.__load__ a classmethod 
       classmethod(cls.__load__)() 
      cls.__loaded_classes.add(cls) 
     return super().__call__(*args, **kwargs) 


class BaseClass(metaclass=StaticLoad): 
    s = 0 


class MyClass(BaseClass): 
    # @classmethod line was deleted 
    def __load__(cls): 
     print("Loading", cls.__name__, "...") 
     cls.s += 1 


obj1 = MyClass() 
obj2 = MyClass() 
print(MyClass.s) 

: 나는 이것을 시도했다.

어떻게하면 잘 작동하도록 내 메타 클래스를 개선해야합니까? 위에서 쓴대로 클래스 BaseClassMyClass의 내용을 유지하고 모든 마법을 StaticLoad에 넣고 싶습니다. @AnttiHaapala의 도움으로

+1

'의 CLS .__ 부하 __()'나'의 CLS .__ 부하 __ (CLS)가'작동하지 않습니다! 놀랍게도 * classmethod 객체 *는 ** 호출 가능하지 않습니다. 그것의'__get__'은 호출 가능한 객체가 클래스 나 인스턴스에서 조회 될 때 그것을 반환합니다. –

+0

나는'cls .__ load __ (cls)'를 호출하려하지 않았다. – Fomalhaut

+0

@AnttiHaapala 그것을'cls .__ load __ (cls)'로 대체하는 것이 도움이되었습니다. 감사합니다. – Fomalhaut

답변

2

솔루션은 간단하다. 대신

classmethod(cls.__load__)() 

를 호출 난 당신이 특정 방법과 클래스 생성의 특성에 변환을 수행하려면

cls.__load__(cls) 
2

전화를했다, 당신은 메타 클래스 '__new__ 기능에 그렇게. 유 이미 메타 클래스를 가지고 있기 때문에

, 당신이해야 할 모든이 classmethod의 모든 __load__ 방법을 변환의 __new__ 방법을 구현하는 것입니다 : 내가 만든

class StaticLoad(type): 
    __loaded_classes = set() 

    def __new__(metacls, name, bases, namespace): 
     if "__load__" in namespace and not isinstance(namespace["__load__"], classmethod): 
      namespace["__load__"] = classmethod(namespace["load"]) 
     return super().__new__(metacls, name, bases, namespace) 

    def __call__(cls, *args, **kwargs): 
     if cls not in cls.__class__.__loaded_classes: 
      if hasattr(cls, '__load__'): 
       cls.__load__() 
      type(cls).__loaded_classes.add(cls) 
     return super().__call__(*args, **kwargs) 

(다른 변화가 EXPLICT을 만드는 것이었다 "그 __loaded_classes "는 클래스 자체가 아니라 메타 클래스에서 액세스해야합니다.

+0

"기타 변경 사항"이 불완전합니다. 'if' 문에서'cls .__ loaded_classes'의 사용을 놓쳤습니다. 더블 언더 밑줄은 메타 클래스 이름을 접두사로 사용하여 속성 이름에서 이름을 변경하는 것이므로 변경이 필요하지 않다고 생각합니다. 그것은 클래스가 그것을 덮어 쓰지 않을 것임을 의미합니다. – Blckknght

+0

예. 나는 거기도 바 꾸었습니다. 나는 이중 밑줄을 지켰으나 이름 변환을 전혀 사용하지 않았다. 또 다른 옵션은'cls .__ class__' 대신 맨손의'__class__'를 사용하는 것이다. – jsbueno

+0

이것은 mangling이 설계된 상황과 정확히 일치합니다. 메타 클래스는 인스턴스의 속성 (일반 클래스)에 유용한 이름을 미리 알 수 없습니다. 자체적 인 구현을 위해 속성을 사용하고자하는 경우에는 이름 앞에 이중 밑줄이 붙은 이름을 사용하여 이름이 인스턴스가 다른 용도로 사용하고있는 이름과 우연히 충돌하지 않도록 할 수 있습니다. 'cls '에 관해서는.__class__'vesus'type (cls)'또는'__class__'를 사용한다면 메타 클래스의 이름을 StaticLoad .__ loaded_classes로 명시하는 것이 더 좋습니다. – Blckknght

관련 문제