2010-04-26 5 views
4

목표 변수를 수정하려면 다음이에서 사용되는 범위를 수정할 수있는 장식을 확인파이썬 데코레이터는 현재 범위에

이 일 경우.

class Blah(): # or perhaps class Blah(ParentClassWhichMakesThisPossible) 

    def one(self): 
     pass 

    @decorated 
    def two(self): 
     pass 

>>> Blah.decorated 
["two"] 

이유는 무엇입니까? 필자는 본질적으로 메서드의 사전을 유지할 수있는 클래스를 작성하여 클래스별로 사용 ​​가능한 여러 메서드 목록을 검색 할 수 있습니다. errr .....

나는이 작업을 수행 할 수 :

class RuleClass(ParentClass): 
    @rule 
    def blah(self): 
     pass 

    @rule 
    def kapow(self): 
     pass 

    def shazam(self): 

class OtherRuleClass(ParentClass): 
    @rule 
    def foo(self): 
     pass 

    def bar(self): 
     pass 

>>> RuleClass.rules.keys() 
["blah", "kapow"] 
>>> OtherRuleClass.rules.keys() 
["foo"] 

답변

9

당신은 당신이 (파이썬 2.6) 클래스 장식 또는 메타 클래스로 원하는 것을 할 수 있습니다. 클래스 데코레이터 버전 :

def rule(f): 
    f.rule = True 
    return f 

def getRules(cls): 
    cls.rules = {} 
    for attr, value in cls.__dict__.iteritems(): 
     if getattr(value, 'rule', False): 
      cls.rules[attr] = value 
    return cls 

@getRules 
class RuleClass: 
    @rule 
    def foo(self): 
     pass 

메타 클래스 버전은 다음과 같습니다

def rule(f): 
    f.rule = True 
    return f 

class RuleType(type): 
    def __init__(self, name, bases, attrs): 
     self.rules = {} 
     for attr, value in attrs.iteritems(): 
      if getattr(value, 'rule', False): 
       self.rules[attr] = value 
     super(RuleType, self).__init__(name, bases, attrs) 

class RuleBase(object): 
    __metaclass__ = RuleType 

class RuleClass(RuleBase): 
    @rule 
    def foo(self): 
     pass 

공지 사항이 중 어느 것도 그것이 깨지기 어려운 종종 불가능하기 때문에 당신이 (호출 네임 스페이스를 수정)를 요청 일을 할 것이다. 대신 클래스 장식 자나 메타 클래스의 __init__ 메서드를 통해 클래스를 모두 포스트 프로세스로 처리하고 모든 특성을 검사하고 rules 특성을 채 웁니다. 두 가지의 차이점은 메타 클래스 솔루션이 파이썬 2.5 및 이전 버전 (2.2까지)에서 작동하고 메타 클래스가 상속된다는 것입니다. 데코레이터를 사용하면 하위 클래스가 개별적으로 데코레이터를 적용해야합니다 (규칙 속성을 설정하려는 경우).

두 솔루션 모두 상속을 고려하지 않으므로 메소드를 찾을 때 부모 클래스를 보지 않습니다 규칙으로 표시되거나 부모 클래스 rules 속성을 보지 않습니다. 그것이 당신이 원하는 것이라면 그렇게하기 위해 확장하기가 어렵지 않습니다. 클래스 객체가 클래스 본문이 실행이 완료된 후 을 구축 :

+0

Python 2.6에는 클래스 데코레이터가 없습니다. –

+0

예, 있습니다. 실제로 코드를 게시하기 전에 두 코드를 모두 테스트했습니다. 그들은 Python 2.6에서 잘 작동합니다. –

+0

내 답변에서와 같이'inspect.getmembers'를 사용하도록 클래스 데코레이터 버전을 변경하면 상속을 처리 할 것입니다 - 그러면 내 답을 삭제할 수 있습니다. (대단히 놀랍지 만 ;-)하지만 당신은 더 완벽합니다. . –

4

문제 에는 아직 개체 Blah이없는 시간에 decorated 장식가 호출된다. 가장 간단한 방법은 decorated 정보를 '어딘가에'다른 곳에 숨기는 것입니다. 함수 속성이면 최종 패스 (클래스 데코레이터 또는 메타 클래스)가 원하는 정보를 해당 사전에 추가합니다.

클래스 장식은 간단하지만은 상속되지 않는다 (그래서 그들은 부모 클래스에서 오지 않을 것), 메타 클래스 상속을하는 동안 - 그래서 당신은 상속을 주장하는 경우, 메타 클래스는 그것으로해야합니다 있다. 간단한-첫째, 당신이 당신의 Q의 시작보다는 "DICT"변종에서이 클래스 장식하고 "목록"변종 나중에이 지금

import inspect 

def classdecorator(aclass): 
    decorated = [] 
    for name, value in inspect.getmembers(aclass, inspect.ismethod): 
    if hasattr(value, '_decorated'): 
     decorated.append(name) 
     del value._decorated 
    aclass.decorated = decorated 
    return aclass 

def decorated(afun): 
    afun._decorated = True 
    return afun 

,

@classdecorator 
class Blah(object): 

    def one(self): 
     pass 

    @decorated 
    def two(self): 
     pass 

주는 Q의 첫 번째 부분에서 Blah.decorated 목록을 요청하십시오. 대신에 dict를 작성하십시오. Q의 두 번째 부분에서 요청할 때 위의 코드에서 decorated.append(name)decorated[name] = value으로 변경하고 물론 클래스 장식 자에서 decorated을 초기화하는 것을 의미합니다 빈 목록보다는 빈 사전으로클래스 본문이 구축 된 후 메타 클래스의 변형이 메타 클래스의 __init__은 본질적으로 같은 후 처리를 수행하는 데 사용할 것

은 - 메타 클래스의 __init__는 마지막 인자로 클래스 본문에 해당하는 딕셔너리를 가져옵니다 (하지만 당신은해야합니다 기본 클래스의 유사한 dict 또는 목록을 적절하게 처리하여 상속을 직접 지원할 수 있습니다. 따라서 메타 클래스 접근법은 실제적으로 클래스 장식 자보다 다소 다소 복잡하지만 개념적으로는 대부분의 사람들이 훨씬 더 어려워한다고 느낍니다. 필요한 경우 메타 클래스에 대한 모든 세부 정보를 제공 하겠지만 가능한 경우 간단한 클래스 장식을 사용하는 것이 좋습니다.

관련 문제