2017-11-26 23 views
1

인스턴스화 할 수없는 기본 클래스 (내 단순화 된 예제에서는 BaseFruit)와 동일한 메소드를 공유해야하는 몇 가지 파생 클래스 (예 : Apple 예) (printfuture). 그러나 여러 가지 변형이 가능하므로 어떤 변형을 사용해야하는지 사용자가 선택하도록하십시오 (예 : sadfuturesaddestfuture).python의 메타 기본 클래스에 사용자가 선택한 메소드 추가

printfuture 메서드는 내 모든 파생 클래스에 공통적 인 것으로, 나는 기본 클래스의 __new__ 메서드를 사용하여 변형에 대한 사용자 인수를 catch하고 해당 메서드를 기본 클래스 자체에 할당하는 것이 적절하다고 생각했습니다.

# my custom methods 

def sadfuture(self): 
    """A sad future""" 
    print 'It looks {}!'.format(self.aspect) 
    print 'Mmm yummy !' 

def saddestfuture(self): 
    """A saddest future""" 
    print 'It looks {}'.format(self.aspect) 
    print 'Garbage !' 

# my base class 

class BaseFruit(object): 
    """Fruit base class""" 
    def __new__(cls, *args, **kwargs): 
     setattr(cls, 'printfuture', kwargs['usermethod']) 
     return object.__new__(cls) 

# My class 

class Apple(BaseFruit): 
    """An apple class""" 
    def __init__(self, aspect, usermethod=sadfuture): 
     self.aspect = aspect 


if __name__ == '__main__': 
    goodapple = Apple(aspect='delicious', usermethod=sadfuture) 
    goodapple.printfuture() # ==> ok 
    badapple = Apple(aspect='rotten', usermethod=saddestfuture) 
    goodapple.printfuture() # ==> not ok anymore 
    badapple.printfuture() # ==> ok 

인쇄 :

It looks delicious! 
Mmm yummy ! 
It looks delicious! 
Mmm yummy ! 
It looks rotten 
Garbage ! 

내가 내 기본 클래스와 첫 덮어 것을 이해한다 : 대신 예상되는 동작의

It looks delicious! 
Mmm yummy ! 
It looks delicious 
Garbage ! 
It looks rotten 
Garbage ! 

을 아래의 예에서 작성된 객체가 동작을 변경했습니다. 그래서, 내 주요 질문은 : 기본 클래스에서 내 사용자 지정 메서드를 유지하면서 예상 된 동작을 어떻게 얻을 수 있습니까?

모범 사례에 대한 의견 및 이러한 문제에 대한 적절한 디자인에 대한 의견도 환영합니다.

+0

** 알려주십시오 ** 예상되는 동작은 무엇입니까? ** – monamona

+0

@monamona done! – Delforge

+0

나는 문제가 무엇인지에 대해 강한 의구심을 갖고 있지만 답변을 입력하는 데 잠시 시간이 걸릴 것입니다. – monamona

답변

2

"예상 된"동작은 실제로 실제로 인쇄되는 동작입니다. 그래서 행동은 "당신이 기대하고 있었던"것이 아니라 다른 것입니다. 이유를 알려주십시오 :

여러분이하고있는 일은 여러분이 Apple의 새로운 인스턴스를 만들 때마다 인스턴스화 된 클래스 (이 경우, Apple)에 새로운 메소드를 생성하는 것입니다. 줄 setattr(cls, 'printfuture', kwargs['usermethod'])은 정확히 BaseFruit 또는 그 하위 클래스의 새 인스턴스를 만들 때마다 그 작업을 수행합니다. (그런데이 줄은 단순히 cls.printfuture = kwargs['usermethod'] 일 수 있습니다. 속성 이름이 하드 코드 된 경우 setattr이 필요하지 않습니다.)

그래서, 당신은 Apple의 두 번째 인스턴스를 만들 때 호출 badapple = Apple(aspect='rotten', usermethod=saddestfuture)saddestfuture뿐 아니라 badapple위한 방법,하지만 애플의 인스턴스가되도록 Apple 클래스 saddestfutureprintfuture을합니다.

메타 클래스가 필요하지 않은 고정 - __new__의 코드를 사용하여 대신 인스턴스에 첨부 된 "pseudomethod"를 만들 수 있습니다. 하지만 인스턴스가 생성 된 후, 인스턴스에 대한 참조가있을 때, 인스턴스 생성 전에가 아니라 클래스 자체에 대한 참조 만있을 때 인스턴스에서이 작업을 수행해야합니다.

클래스를 설치하기 전에 실행해야하는 실제 코드가 없으므로 __init__에 메서드와 유사한 함수를 바인딩하고 실제로 필요한 경우에만 __new__ 사용자 지정을 남겨 둘 수 있습니다. 그 순간 동안, 그것은 슈퍼 클래스의 전화를 하드 코딩하는 대신 super를 사용하는 해치지 않을 것입니다 :

... 
# my base class 

class BaseFruit(object): 
    """Fruit base class""" 
    def __init__(self, *args, **kwargs): 
     printfuture = kwargs.pop('usermethod') 
     super(BaseFruit, self).__init__(*args, **kwargs) 
     # Wrap the call to the passed method in a function 
     # that captures "self" from here, since Python do not 
     # insert "self" in calls to functions 
     # attributed to instances. 
     self.printfuture = lambda: printfuture(self) 


# My class 

class Apple(BaseFruit): 
    """An apple class""" 
    def __init__(self, aspect, usermethod=sadfuture): 
     super(Apple, self).__init__(usermethod) 
     self.aspect = aspect 

그리고,이 그들을 필요가 없습니다 메타 클래스에 관해서는 - 반대로, 당신은 각 인스턴스를 사용자 정의 할 수 있습니다 그것은 만들어집니다. 클래스 자체를 사용자 정의해야하는 경우 일반적으로 메타 클래스를 사용합니다. 원래 코드는 그것을하고 있습니다 (클래스 자체를 커스터마이징).하지만, 그 코드는 여러분이 기대하지 않았던 행동을 위해 만들어진 각 인스턴스가 생성 될 때 실행됩니다. printfuture 메소드를 만드는 코드가 메타 클래스 __new__ 메소드에서 대신 슈퍼 클래스에있는 것과 같지 않은 경우, 각 서브 클래스가 선언되고 (해당 서브 클래스의 모든 인스턴스가 동일하게 printifuture을 공유 할 때) 방법).

이제는 어떻게 작동하는지 파악한 후,이 내용을 계속 배우기 위해 Python 3로 이동하십시오. 파이썬 2는 지금부터 2 년 후에 완전히 끝나게 될 것이며, 어떤 프로 젝트에서도 쓸모 없을 것입니다. 한 가지는 파이썬 2에서 레거시 코드를 유지해야하는 것이고 다른 하나는 새로운 프로젝트를 배우거나 시작하는 것입니다. 파이썬 3 만 사용해야합니다.

0

문제는 기본 클래스에서 발생한다고 생각합니다.

BaseFruit을 사용하여 Apple 클래스의 기본 클래스로 사용하면 Python은 Apple 클래스 및 BaseFruit 클래스에있는 값을 BaseFruit 클래스에 직접 할당합니다. 두 '사과'는 동일한 기본 클래스를 기반으로하므로이 클래스의 값을 공유합니다.

saddestfuture을 실행할 기능으로 설정하면 BaseFruit-Class 기반의 모든 개체에 대해 '전역 적'으로 설정됩니다.

당신은 apple__init__에서 선

self.userm = usermethod 

이 필요합니다. 그런 다음이 self.usermkwarg으로 BaseClass에 전달하고 "usermethod" 문자열은 전달하지 않습니다.

오랫동안 파이썬 상속 규칙을 사용하지 않았기 때문에 excatly 구문을 알지 못합니다. 구문을 잊어 버렸습니다. 어쩌면 다른 사람이 주석에 코드를 제안 할 수도 있고 독자가 직접 찾아 낼 수도 있습니다 :-).