2017-04-18 9 views
0

나는 꽤 간단한 문제라고 생각하지만 만족스러운 답을 찾을 수 없었다. 간단히 말해, 각 하위 클래스에 논리를 추가하지 않고 부모 클래스의 하위 클래스에 대한 계약을 시행하고자합니다. 아래 코드 예제 :파이썬에서 하위 클래스의 함수 장식하기

class A(object): 

    @abc.abstractmethod 
    def do_thing(self, input): 
     raise NotImplementedError 

class A1(A): 

    def do_thing_decorator(self, do_thing_func): 
     def checked_do_thing(input): 
      check = do_thing_func(input) 
      if check != 1: 
       raise ValueError 
      return check 
     return checked_do_thing 

그래서 질문은 내가 A1에서 상속 클래스에 의해 구현 된 do_thing 기능을 자동 장식 어떻게입니까? 약간 다른 수표를 사용하여 A2 클래스가 있다고 가정합니다.

초기 조사에서는 메타 아크릴을 사용하는 방법이지만 작동 원리에 대한 설명이 어려웠 음을 의미합니다. Python 2.x에서 작동하는 이상적인 기능을 찾고 있지만, 3.x 솔루션 만 있으면 코드베이스를 변경해도 행복합니다.

+1

'__new __()'메소드를 보셨습니까? –

+0

@ IgnacioVazquez-Abrams는 불합리한 해결책이 아닙니다. 메소드 레벨에서 좀 더 직관적 인 것을 기대하고 있었지만, 뭔가 더 좋은 것을 찾을 수 없다면 아마도 디폴트가 될 것입니다. –

답변

2

우선 : abc 모듈을 잘못 사용하고 있습니다 (see the docs). 수업 Aabc.ABCMeta의 메타 클래스를 가져야합니다. 그래서 이미 메타 클래스를 사용하고 있다면 그것을 당신의 장점으로 확장 할 수 있습니다.

abstractmethod 작동하도록 abc.ABCMeta에서 상속 및 do_thing 장식 메타 클래스 :

# class Abstract(metaclass=ABCMeta): in python3 
class Abstract(object): 
    __metaclass__ = DecoratingMeta # remove this line in python3 
    @abstractmethod 
    def do_thing(self, input): 
     pass 

    @classmethod 
    def do_thing_decorator(cls, function): 
     return function  # default decorator that does nothing 

참고 do_thing_decorator : 아무것도하지 기본 체크 장식과 함께 이제 추상 기본 클래스를

from abc import ABCMeta, abstractmethod 

class DecoratingMeta(ABCMeta): 
    def __new__(cls, *args): 
     new_class = super(DecoratingMeta, cls).__new__(cls, *args) 
     # decorating do_thing manually 
     new_class.do_thing = new_class.do_thing_decorator(new_class.do_thing) 
     return new_class 

을 이 경우 클래스 메소드 여야합니다. python3python2에서 작동하는 메타 클래스의 경우 six을 참조하십시오. 단지 특정 검사를 구현하지만 여전히 추상적 인

귀하의 검사 클래스 : 당신이 check = do_thing_func(input)을 쓴 라인은 재귀 오류가 발생할 것이라고

class Checker(Abstract): 
    @classmethod 
    def do_thing_decorator(cls, function): 
     def do_checked_thing(self, input): 
      check = function(self, input) # NOT self.do_thing(input) else recursion error 
      if check != 1: 
       raise ValueError("Check failed") 
      return check 
     return do_checked_thing 

참고.

그리고 do_thing의 샘플 구현하여 구체적인 클래스 :

class Concrete(Checker): 
    def do_thing(self, input): 
     return input # sample implementation 

당신은 do_thing(1) 성공 확인할 수 있습니다 및 do_thing(2)이 방식의 단점은 당신이 할 수 없다는 것입니다

c = Concrete() 

c.do_thing(1) 
try: 
    c.do_thing(2) 
except ValueError: 
    print("check failed") 

실패 do_thing_decorator 개요.

그래서이 이미 많은 텍스트,하지만 당신은 전혀 메타 클래스를 사용하지 않으려면 훨씬 더 간단한 방법이있다 :

하여 do_thing 방법의 검사를 수행하는 클래스를 작성 이 "추상적"방법을 사용하여 : do_thing_funccheck_do_thing 정말 추상적하지 않은 것을

class Abstract(object): 
    def do_thing_func(self, input): 
     raise NotImplementedError() 

    def check_do_thing(self, result): 
     raise NotImplementedError() 

    # don't override this method 
    def do_thing(self, input): 
     result = self.do_thing_func(input) 
     self.check_do_thing(result) # may raise 
     return result # if it does not raise return the result 

참고하면 유형 Abstract의 아직 실체화 객체를 할 수 있습니다. 당신이 추상적 인 것을 원한다면 표준 abc.ABCMeta 메타 클래스를 여기에 사용하십시오.

지금 우리가 여기에 장식을 필요로하지 않기 때문에 훨씬 간단하게 check_do_thing

class Checker(Abstract): 
    def check_do_thing(self, result): 
     if result != 1: 
      raise ValueError("check failed") 

구현하는 검사 클래스를 만들 수 있습니다.

그리고 Concrete 지금 do_thing_func를 구현해야하지만이 클래스를 사용할 때 do_thing를 호출해야한다는 do_thing_func

class Concrete(Checker): 
    def do_thing_func(self, input): 
     return input # sample implementation 

참고 구현 마지막으로 구상 클래스

.

여기서 단점은 do_thing을 무시하고 검사를 위반할 수 있다는 것입니다.

관련 문제