[python] 인스턴스 수준에서 메서드 재정의

Python에서 인스턴스 수준에서 클래스 메서드를 재정의하는 방법이 있습니까? 예를 들면 :

class Dog:
    def bark(self):
        print "WOOF"

boby = Dog()
boby.bark() # WOOF
# METHOD OVERRIDE
boby.bark() # WoOoOoF!!



답변

표시된대로이 작업을 수행하지 마십시오. 인스턴스를 클래스와 다른 것으로 monkeypatch하면 코드를 읽을 수 없게됩니다.

monkeypatched 코드를 디버깅 할 수 없습니다.

bobyprint type(boby)에서 버그를 발견하면 (a) 개이지만 (b) 일부 모호한 이유로 제대로 짖지 않는다는 것을 알 수 있습니다. 이것은 악몽입니다. 하지마.

대신 이것을하십시오.

class Dog:
    def bark(self):
        print "WOOF"

class BobyDog( Dog ):
    def bark( self ):
        print "WoOoOoF!!"

otherDog= Dog()
otherDog.bark() # WOOF

boby = BobyDog()
boby.bark() # WoOoOoF!!


답변

예, 가능합니다 :

class Dog:
    def bark(self):
        print "Woof"

def new_bark(self):
    print "Woof Woof"

foo = Dog()

funcType = type(Dog.bark)

# "Woof"
foo.bark()

# replace bark with new_bark for this object only
foo.bark = funcType(new_bark, foo, Dog)

foo.bark()
# "Woof Woof"


답변

모듈 에서 MethodType 을 사용해야 types합니다. 의 목적은 MethodType인스턴스 수준 메서드를 덮어 쓰는 것입니다 ( self덮어 쓴 메서드에서 사용할 수 있음).

아래 예를 참조하십시오.

import types

class Dog:
    def bark(self):
        print "WOOF"

boby = Dog()
boby.bark() # WOOF

def _bark(self):
    print "WoOoOoF!!"

boby.bark = types.MethodType(_bark, boby)

boby.bark() # WoOoOoF!!


답변

@codelogic의 탁월한 대답을 설명하기 위해 더 명시적인 접근 방식을 제안합니다. 이는 .인스턴스 속성으로 액세스 할 때 연산자가 클래스 메서드를 바인딩하기 위해 철저히 수행 하는 동일한 기술입니다. 단, 메서드는 실제로 클래스 외부에서 정의 된 함수가됩니다.

@codelogic의 코드로 작업 할 때 유일한 차이점은 메서드가 바인딩되는 방식입니다. 함수와 메서드가 Python에서 데이터가 아닌 설명 자라는 사실을 사용하고 메서드 를 호출합니다 __get__. 특히 원본과 대체 모두 동일한 서명을 가지고 있습니다. 즉, 대체를 전체 클래스 메서드로 작성하여 .NET을 통해 모든 인스턴스 속성에 액세스 할 수 있습니다 self.

클래스 Dog :
    def bark (self) :
        "Woof"인쇄

def new_bark (self) :
    "Woof Woof"인쇄

foo = Dog ()

# "멍청이"
foo.bark ()

#이 개체에 대해서만 bark를 new_bark로 바꿉니다.
foo.bark = new_bark .__ get __ (foo, Dog)

foo.bark ()
# "Woof Woof"

바인딩 된 메서드를 인스턴스 속성에 할당하여 메서드 재정의에 대한 거의 완전한 시뮬레이션을 만들었습니다. 누락 된 편리한 기능 중 하나 super는 클래스 정의에 있지 않기 때문에 인수가없는 버전에 대한 액세스 입니다. 또 다른 한 가지는 __name__바인딩 된 메서드 의 속성이 클래스 정의 에서처럼 재정의하는 함수의 이름을 사용하지 않지만 수동으로 설정할 수 있다는 것입니다. 세 번째 차이점은 수동으로 바인딩 된 메서드가 함수 인 일반 속성 참조라는 것입니다. .운영자는 아무것도하지 않습니다하지만 참조를 가져옵니다. 반면에 인스턴스에서 일반 메서드를 호출 할 때 바인딩 프로세스는 매번 새로운 바인딩 된 메서드를 만듭니다.

그런데 이것이 작동하는 유일한 이유는 인스턴스 속성이 비 데이터 설명자를 재정의하기 때문 입니다. 데이터 디스크립터에는 __set__메서드가 있지만 (다행히도) 메서드에는 없습니다. 클래스의 데이터 설명자는 실제로 모든 인스턴스 속성보다 우선합니다. 이것이 속성에 할당 할 수있는 이유입니다. 할당을 __set__시도 할 때 해당 메서드가 호출됩니다. 저는 개인적으로 이것을 한 단계 더 나아가 인스턴스의 기본 속성의 실제 값을 숨기고 싶습니다 __dict__. 속성이 그림자를 가리기 때문에 정상적인 방법으로는 액세스 할 수 없습니다.

이것은 매직 (이중 밑줄) 메서드 에는 무의미하다는 것을 명심해야 합니다 . 물론 매직 메서드는 이런 방식으로 재정의 될 수 있지만이를 사용하는 작업은 유형 만 확인합니다. 예를 들어 __contains__인스턴스에서 특별한 것을 설정할 수 있지만 호출 x in instance하면이를 무시하고 type(instance).__contains__(instance, x)대신 사용 합니다. 이것은 Python 데이터 모델에 지정된 모든 매직 메서드에 적용됩니다 .


답변

class Dog:
    def bark(self):
        print "WOOF"

boby = Dog()
boby.bark() # WOOF

# METHOD OVERRIDE
def new_bark():
    print "WoOoOoF!!"
boby.bark = new_bark

boby.bark() # WoOoOoF!!

boby필요한 경우 함수 내 에서 변수 를 사용할 수 있습니다 . 이 하나의 인스턴스 객체에 대해서만 메서드를 재정의하기 때문에이 방법이 더 간단하고를 사용하는 것과 정확히 동일한 효과가 self있습니다.


답변

functools.partial여기 에 언급 된 사람이 없기 때문에 :

from functools import partial

class Dog:
    name = "aaa"
    def bark(self):
        print("WOOF")

boby = Dog()
boby.bark() # WOOF

def _bark(self):
    print("WoOoOoF!!")

boby.bark = partial(_bark, boby)
boby.bark() # WoOoOoF!!


답변

함수는 Python에서 첫 번째 클래스 객체이므로 클래스 객체를 초기화하는 동안 전달하거나 주어진 클래스 인스턴스에 대해 언제든지 재정의 할 수 있습니다.

class Dog:
    def __init__(self,  barkmethod=None):
        self.bark=self.barkp
        if barkmethod:
           self.bark=barkmethod
    def barkp(self):
        print "woof"

d=Dog()
print "calling original bark"
d.bark()

def barknew():
    print "wooOOOoof"

d1=Dog(barknew)
print "calling the new bark"
d1.bark()

def barknew1():
    print "nowoof"

d1.bark=barknew1
print "calling another new"
d1.bark()

결과는

calling original bark
woof
calling the new bark
wooOOOoof
calling another new
nowoof