Python에서 인스턴스 수준에서 클래스 메서드를 재정의하는 방법이 있습니까? 예를 들면 :
class Dog:
def bark(self):
print "WOOF"
boby = Dog()
boby.bark() # WOOF
# METHOD OVERRIDE
boby.bark() # WoOoOoF!!
답변
표시된대로이 작업을 수행하지 마십시오. 인스턴스를 클래스와 다른 것으로 monkeypatch하면 코드를 읽을 수 없게됩니다.
monkeypatched 코드를 디버깅 할 수 없습니다.
boby
및 print 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