답변:
파이썬에서는 함수와 바운드 메소드간에 차이가 있습니다.
>>> def foo():
... print "foo"
...
>>> class A:
... def bar( self ):
... print "bar"
...
>>> a = A()
>>> foo
<function foo at 0x00A98D70>
>>> a.bar
<bound method A.bar of <__main__.A instance at 0x00A9BC88>>
>>>
바운드 메서드는 인스턴스에 "연결되어"있지만 (설명 적) 메서드가 호출 될 때마다 해당 인스턴스가 첫 번째 인수로 전달됩니다.
그러나 인스턴스의 속성과 달리 클래스의 속성 인 콜 러블은 여전히 바인딩되어 있지 않으므로 언제든지 원하는 경우 클래스 정의를 수정할 수 있습니다.
>>> def fooFighters( self ):
... print "fooFighters"
...
>>> A.fooFighters = fooFighters
>>> a2 = A()
>>> a2.fooFighters
<bound method A.fooFighters of <__main__.A instance at 0x00A9BEB8>>
>>> a2.fooFighters()
fooFighters
이전에 정의 된 인스턴스도 속성 자체를 재정의하지 않는 한 업데이트됩니다.
>>> a.fooFighters()
fooFighters
단일 인스턴스에 메소드를 연결하려고 할 때 문제가 발생합니다.
>>> def barFighters( self ):
... print "barFighters"
...
>>> a.barFighters = barFighters
>>> a.barFighters()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: barFighters() takes exactly 1 argument (0 given)
이 함수는 인스턴스에 직접 연결될 때 자동으로 바인딩되지 않습니다.
>>> a.barFighters
<function barFighters at 0x00A98EF0>
이를 바인딩하기 위해 types 모듈에서 MethodType 함수를 사용할 수 있습니다 .
>>> import types
>>> a.barFighters = types.MethodType( barFighters, a )
>>> a.barFighters
<bound method ?.barFighters of <__main__.A instance at 0x00A9BC88>>
>>> a.barFighters()
barFighters
이번에는 클래스의 다른 인스턴스가 영향을받지 않았습니다.
>>> a2.barFighters()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: A instance has no attribute 'barFighters'
descriptor protocol
vs 를 사용하는 것의 이점을 제공합니다 MethodType
.
classmethod
및 staticmethod
다른 설명도. 또 다른 가져 오기로 네임 스페이스를 복잡하게 만들지 않습니다.
a.barFighters = barFighters.__get__(a)
새로운 모듈 은 파이썬 2.6부터 더 이상 사용되지 않으며 3.0에서 제거되었습니다. 유형을 사용하십시오.
참조 http://docs.python.org/library/new.html를
아래 예에서 의도적으로 patch_me()
함수 에서 반환 값을 제거했습니다 . 나는 반환 값을 주면 패치가 새로운 객체를 반환한다고 믿게 만들 수 있다고 생각합니다. 아마도 이것은 더 잘 훈련 된 몽키 패칭의 사용을 촉진 할 수 있습니다.
import types
class A(object):#but seems to work for old style objects too
pass
def patch_me(target):
def method(target,x):
print "x=",x
print "called from", target
target.method = types.MethodType(method,target)
#add more if needed
a = A()
print a
#out: <__main__.A object at 0x2b73ac88bfd0>
patch_me(a) #patch instance
a.method(5)
#out: x= 5
#out: called from <__main__.A object at 0x2b73ac88bfd0>
patch_me(A)
A.method(6) #can patch class too
#out: x= 6
#out: called from <class '__main__.A'>
서문-호환성에 대한 참고 사항 : 다른 답변은 Python 2에서만 작동 할 수 있습니다.이 답변은 Python 2 및 3에서 완벽하게 작동합니다. Python 3 만 작성하는 경우 명시 적으로 상속 object
하지 않을 수 있지만 코드가 동일하게 유지되어야합니다 .
기존 객체 인스턴스에 메소드 추가
파이썬에서 기존 객체 (예 : 클래스 정의가 아닌)에 메소드를 추가하는 것이 가능하다는 것을 읽었습니다.
그렇게하는 것이 항상 좋은 결정은 아니라는 것을 이해합니다. 그러나 어떻게 이것을 할 수 있습니까?
나는 이것을 권장하지 않습니다. 이것은 나쁜 생각입니다. 하지마
몇 가지 이유는 다음과 같습니다.
따라서 나는 당신이 정말로 좋은 이유가 없다면 이것을하지 말 것을 권한다. 이 클래스 정의에 올바른 방법을 정의하는 데 훨씬 더 또는 덜 같이, 바람직하게는 원숭이 패치 클래스를 직접 :
Foo.sample_method = sample_method
그러나 유익한 방법이므로이 작업을 수행하는 몇 가지 방법을 보여 드리겠습니다.
다음은 몇 가지 설정 코드입니다. 클래스 정의가 필요합니다. 가져올 수는 있지만 실제로는 중요하지 않습니다.
class Foo(object):
'''An empty class to demonstrate adding a method to an instance'''
인스턴스를 만듭니다.
foo = Foo()
추가 할 메소드를 작성하십시오.
def sample_method(self, bar, baz):
print(bar + baz)
__get__
함수에 대한 점선 조회 __get__
는 인스턴스와 함께 함수 의 메서드를 호출하여 개체를 메서드에 바인딩하여 "바운드 메서드"를 만듭니다.
foo.sample_method = sample_method.__get__(foo)
그리고 지금:
>>> foo.sample_method(1,2)
3
먼저 가져 오기 유형을 통해 메소드 생성자를 가져옵니다.
import types
이제 메소드를 인스턴스에 추가합니다. 이를 위해 types
모듈 에서 MethodType 생성자가 필요합니다 (위에서 가져옴).
types.MethodType의 인수 서명은 (function, instance, class)
다음과 같습니다.
foo.sample_method = types.MethodType(sample_method, foo, Foo)
그리고 사용법 :
>>> foo.sample_method(1,2)
3
먼저 메서드를 인스턴스에 바인딩하는 래퍼 함수를 만듭니다.
def bind(instance, method):
def binding_scope_fn(*args, **kwargs):
return method(instance, *args, **kwargs)
return binding_scope_fn
용법:
>>> foo.sample_method = bind(foo, sample_method)
>>> foo.sample_method(1,2)
3
부분 함수는 첫 번째 인수를 함수 (및 선택적으로 키워드 인수)에 적용하고 나중에 나머지 인수와 함께 키워드 인수를 재정 의하여 호출 할 수 있습니다. 그러므로:
>>> from functools import partial
>>> foo.sample_method = partial(sample_method, foo)
>>> foo.sample_method(1,2)
3
바인딩 된 메서드가 인스턴스의 부분 함수라는 것을 고려할 때 이치에 맞습니다.
sample_method를 클래스에 추가 할 때와 같은 방법으로 추가하려고하면 인스턴스에서 바인딩되지 않으며 암시 적 자체를 첫 번째 인수로 사용하지 않습니다.
>>> foo.sample_method = sample_method
>>> foo.sample_method(1,2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: sample_method() takes exactly 3 arguments (2 given)
인스턴스를 명시 적으로 전달하여 바인딩되지 않은 함수를 작동시킬 수 있습니다 (또는이 메소드는 실제로 self
인수 변수를 사용하지 않기 때문에 ). 다른 인스턴스의 예상 서명과 일치하지 않습니다 (원숭이 패치 인 경우) 이 예) :
>>> foo.sample_method(foo, 1, 2)
3
당신은 지금 당신 이 이것을 할 수 있는 몇 가지 방법을 알고 있지만, 진지하게-이것을하지 마십시오.
__get__
메소드에는 다음 매개 변수로 클래스가 필요합니다 sample_method.__get__(foo, Foo)
.
위의 답변이 요점을 놓쳤다 고 생각합니다.
메소드가있는 클래스를 보자.
class A(object):
def m(self):
pass
이제 ipython에서 게임 해 봅시다 :
In [2]: A.m
Out[2]: <unbound method A.m>
그래서 m ()은 어떻게 든 A 의 바인딩되지 않은 메소드가 됩니다. 하지만 정말 그런가요?
In [5]: A.__dict__['m']
Out[5]: <function m at 0xa66b8b4>
그것은 밝혀 m은 () 에 추가 참조되는 단지 기능입니다 의 마술은 없다 - 클래스 사전. 그렇다면 Am 은 왜 우리에게 언 바운드 방법을 제공합니까? 점이 간단한 사전 조회로 변환되지 않았기 때문입니다. 실제로 A .__ class __.__ getattribute __ (A, 'm')의 호출입니다.
In [11]: class MetaA(type):
....: def __getattribute__(self, attr_name):
....: print str(self), '-', attr_name
In [12]: class A(object):
....: __metaclass__ = MetaA
In [23]: A.m
<class '__main__.A'> - m
<class '__main__.A'> - m
이제 왜 마지막 줄이 두 번 인쇄되는지 머리 꼭대기에서 잘 모르겠지만 여전히 무슨 일이 일어나고 있는지 분명합니다.
이제 기본 __getattribute__가하는 것은 속성이 소위 디스크립터 인지 여부, 즉 특수 __get__ 메소드를 구현하는지 여부를 확인하는 것입니다. 해당 메소드를 구현하면 해당 __get__ 메소드를 호출 한 결과가 리턴됩니다. A 클래스 의 첫 번째 버전으로 돌아 가면 다음과 같습니다.
In [28]: A.__dict__['m'].__get__(None, A)
Out[28]: <unbound method A.m>
파이썬 함수는 디스크립터 프로토콜을 구현하기 때문에 객체 대신 호출하면 __get__ 메소드에서 해당 객체에 바인딩됩니다.
그렇다면 기존 객체에 메소드를 추가하는 방법은 무엇입니까? 패치 클래스를 신경 쓰지 않는다고 가정하면 다음과 같이 간단합니다.
B.m = m
그런 다음 Bm 은 설명자 마술 덕분에 바인딩되지 않은 방법이되었습니다.
그리고 단일 객체에만 메소드를 추가하려면 types.MethodType을 사용하여 기계를 직접 에뮬레이션해야합니다.
b.m = types.MethodType(m, b)
그건 그렇고 :
In [2]: A.m
Out[2]: <unbound method A.m>
In [59]: type(A.m)
Out[59]: <type 'instancemethod'>
In [60]: type(b.m)
Out[60]: <type 'instancemethod'>
In [61]: types.MethodType
Out[61]: <type 'instancemethod'>
파이썬에서 원숭이 패치는 일반적으로 자신의 클래스 또는 함수 서명을 덮어 쓰면 작동합니다. 다음은 Zope Wiki 의 예입니다 .
from SomeOtherProduct.SomeModule import SomeClass
def speak(self):
return "ook ook eee eee eee!"
SomeClass.speak = speak
이 코드는 클래스에서 speak라는 메소드를 덮어 쓰거나 작성합니다. 원숭이 패치에 관한 Jeff Atwood의 최근 게시물에서 . 그는 현재 업무에 사용하는 언어 인 C # 3.0의 예를 보여줍니다.
람다를 사용하여 메소드를 인스턴스에 바인딩 할 수 있습니다.
def run(self):
print self._instanceString
class A(object):
def __init__(self):
self._instanceString = "This is instance string"
a = A()
a.run = lambda: run(a)
a.run()
산출:
This is instance string
메소드없이 인스턴스에 메소드를 첨부하는 방법은 두 가지 이상 있습니다 types.MethodType
.
>>> class A:
... def m(self):
... print 'im m, invoked with: ', self
>>> a = A()
>>> a.m()
im m, invoked with: <__main__.A instance at 0x973ec6c>
>>> a.m
<bound method A.m of <__main__.A instance at 0x973ec6c>>
>>>
>>> def foo(firstargument):
... print 'im foo, invoked with: ', firstargument
>>> foo
<function foo at 0x978548c>
1:
>>> a.foo = foo.__get__(a, A) # or foo.__get__(a, type(a))
>>> a.foo()
im foo, invoked with: <__main__.A instance at 0x973ec6c>
>>> a.foo
<bound method A.foo of <__main__.A instance at 0x973ec6c>>
2 :
>>> instancemethod = type(A.m)
>>> instancemethod
<type 'instancemethod'>
>>> a.foo2 = instancemethod(foo, a, type(a))
>>> a.foo2()
im foo, invoked with: <__main__.A instance at 0x973ec6c>
>>> a.foo2
<bound method instance.foo of <__main__.A instance at 0x973ec6c>>
유용한 링크 :
데이터 모델-디스크립터 호출 설명자
사용법 안내서-디스크립터 호출
당신이 찾고있는 것은 setattr
내가 믿는 것입니다. 이것을 사용하여 객체의 속성을 설정하십시오.
>>> def printme(s): print repr(s)
>>> class A: pass
>>> setattr(A,'printme',printme)
>>> a = A()
>>> a.printme() # s becomes the implicit 'self' variable
< __ main __ . A instance at 0xABCDEFG>
A
인스턴스가 아닌 클래스를 패치하고 a
있습니다.
setattr(A,'printme',printme)
단순히 대신 사용해야 할 이유가 A.printme = printme
있습니까?
Jason Pratt와 커뮤니티 위키 답변을 통합하여 다양한 바인딩 방법의 결과를 살펴보십시오.
특히 바인딩 메소드를 클래스 메소드로 추가하는 방법이 작동 하지만 참조 범위가 올바르지 않습니다.
#!/usr/bin/python -u
import types
import inspect
## dynamically adding methods to a unique instance of a class
# get a list of a class's method type attributes
def listattr(c):
for m in [(n, v) for n, v in inspect.getmembers(c, inspect.ismethod) if isinstance(v,types.MethodType)]:
print m[0], m[1]
# externally bind a function as a method of an instance of a class
def ADDMETHOD(c, method, name):
c.__dict__[name] = types.MethodType(method, c)
class C():
r = 10 # class attribute variable to test bound scope
def __init__(self):
pass
#internally bind a function as a method of self's class -- note that this one has issues!
def addmethod(self, method, name):
self.__dict__[name] = types.MethodType( method, self.__class__ )
# predfined function to compare with
def f0(self, x):
print 'f0\tx = %d\tr = %d' % ( x, self.r)
a = C() # created before modified instnace
b = C() # modified instnace
def f1(self, x): # bind internally
print 'f1\tx = %d\tr = %d' % ( x, self.r )
def f2( self, x): # add to class instance's .__dict__ as method type
print 'f2\tx = %d\tr = %d' % ( x, self.r )
def f3( self, x): # assign to class as method type
print 'f3\tx = %d\tr = %d' % ( x, self.r )
def f4( self, x): # add to class instance's .__dict__ using a general function
print 'f4\tx = %d\tr = %d' % ( x, self.r )
b.addmethod(f1, 'f1')
b.__dict__['f2'] = types.MethodType( f2, b)
b.f3 = types.MethodType( f3, b)
ADDMETHOD(b, f4, 'f4')
b.f0(0) # OUT: f0 x = 0 r = 10
b.f1(1) # OUT: f1 x = 1 r = 10
b.f2(2) # OUT: f2 x = 2 r = 10
b.f3(3) # OUT: f3 x = 3 r = 10
b.f4(4) # OUT: f4 x = 4 r = 10
k = 2
print 'changing b.r from {0} to {1}'.format(b.r, k)
b.r = k
print 'new b.r = {0}'.format(b.r)
b.f0(0) # OUT: f0 x = 0 r = 2
b.f1(1) # OUT: f1 x = 1 r = 10 !!!!!!!!!
b.f2(2) # OUT: f2 x = 2 r = 2
b.f3(3) # OUT: f3 x = 3 r = 2
b.f4(4) # OUT: f4 x = 4 r = 2
c = C() # created after modifying instance
# let's have a look at each instance's method type attributes
print '\nattributes of a:'
listattr(a)
# OUT:
# attributes of a:
# __init__ <bound method C.__init__ of <__main__.C instance at 0x000000000230FD88>>
# addmethod <bound method C.addmethod of <__main__.C instance at 0x000000000230FD88>>
# f0 <bound method C.f0 of <__main__.C instance at 0x000000000230FD88>>
print '\nattributes of b:'
listattr(b)
# OUT:
# attributes of b:
# __init__ <bound method C.__init__ of <__main__.C instance at 0x000000000230FE08>>
# addmethod <bound method C.addmethod of <__main__.C instance at 0x000000000230FE08>>
# f0 <bound method C.f0 of <__main__.C instance at 0x000000000230FE08>>
# f1 <bound method ?.f1 of <class __main__.C at 0x000000000237AB28>>
# f2 <bound method ?.f2 of <__main__.C instance at 0x000000000230FE08>>
# f3 <bound method ?.f3 of <__main__.C instance at 0x000000000230FE08>>
# f4 <bound method ?.f4 of <__main__.C instance at 0x000000000230FE08>>
print '\nattributes of c:'
listattr(c)
# OUT:
# attributes of c:
# __init__ <bound method C.__init__ of <__main__.C instance at 0x0000000002313108>>
# addmethod <bound method C.addmethod of <__main__.C instance at 0x0000000002313108>>
# f0 <bound method C.f0 of <__main__.C instance at 0x0000000002313108>>
개인적으로 외부 ADDMETHOD 함수 경로를 선호합니다. 이터레이터 내에서 새로운 메소드 이름을 동적으로 할당 할 수 있기 때문입니다.
def y(self, x):
pass
d = C()
for i in range(1,5):
ADDMETHOD(d, y, 'f%d' % i)
print '\nattributes of d:'
listattr(d)
# OUT:
# attributes of d:
# __init__ <bound method C.__init__ of <__main__.C instance at 0x0000000002303508>>
# addmethod <bound method C.addmethod of <__main__.C instance at 0x0000000002303508>>
# f0 <bound method C.f0 of <__main__.C instance at 0x0000000002303508>>
# f1 <bound method ?.y of <__main__.C instance at 0x0000000002303508>>
# f2 <bound method ?.y of <__main__.C instance at 0x0000000002303508>>
# f3 <bound method ?.y of <__main__.C instance at 0x0000000002303508>>
# f4 <bound method ?.y of <__main__.C instance at 0x0000000002303508>>
addmethod
다음과 같이 다시 작성 def addmethod(self, method, name): self.__dict__[name] = types.MethodType( method, self )
하면 문제가 해결됩니다
Jason의 답변은 작동하지만 클래스에 함수를 추가하려는 경우에만 작동합니다. .py 소스 코드 파일에서 이미 기존 메소드를 다시로드하려고 시도했을 때 작동하지 않았습니다.
해결 방법을 찾는 데 오랜 시간이 걸렸지 만 트릭은 간단 해 보입니다 ... 1. 소스 코드 파일에서 코드를 가져옵니다. 2.nd reforce 3.rd use types.FunctionType (...) 재로드 된 메소드가 다른 네임 스페이스에 있기 때문에 현재 전역 변수를 전달할 수있는 함수에 가져 오기 및 바인딩 된 메소드도 있습니다. 이제는 "Jason Pratt"가 types.MethodType (... )
예:
# this class resides inside ReloadCodeDemo.py
class A:
def bar( self ):
print "bar1"
def reloadCode(self, methodName):
''' use this function to reload any function of class A'''
import types
import ReloadCodeDemo as ReloadMod # import the code as module
reload (ReloadMod) # force a reload of the module
myM = getattr(ReloadMod.A,methodName) #get reloaded Method
myTempFunc = types.FunctionType(# convert the method to a simple function
myM.im_func.func_code, #the methods code
globals(), # globals to use
argdefs=myM.im_func.func_defaults # default values for variables if any
)
myNewM = types.MethodType(myTempFunc,self,self.__class__) #convert the function to a method
setattr(self,methodName,myNewM) # add the method to the function
if __name__ == '__main__':
a = A()
a.bar()
# now change your code and save the file
a.reloadCode('bar') # reloads the file
a.bar() # now executes the reloaded code
이 질문은 몇 년 전에 시작되었지만 데코레이터를 사용하여 클래스 인스턴스에 함수의 바인딩을 시뮬레이션하는 쉬운 방법이 있습니다.
def binder (function, instance):
copy_of_function = type (function) (function.func_code, {})
copy_of_function.__bind_to__ = instance
def bound_function (*args, **kwargs):
return copy_of_function (copy_of_function.__bind_to__, *args, **kwargs)
return bound_function
class SupaClass (object):
def __init__ (self):
self.supaAttribute = 42
def new_method (self):
print self.supaAttribute
supaInstance = SupaClass ()
supaInstance.supMethod = binder (new_method, supaInstance)
otherInstance = SupaClass ()
otherInstance.supaAttribute = 72
otherInstance.supMethod = binder (new_method, otherInstance)
otherInstance.supMethod ()
supaInstance.supMethod ()
거기에서 함수와 인스턴스를 바인더 데코레이터에 전달하면 첫 번째 함수와 동일한 코드 객체를 사용하여 새 함수가 생성됩니다. 그런 다음 지정된 클래스 인스턴스가 새로 작성된 함수의 속성에 저장됩니다. 데코레이터는 복사 된 함수를 자동으로 호출하는 (세번째) 함수를 반환하여 인스턴스를 첫 번째 매개 변수로 제공합니다.
결론적으로 클래스 인스턴스에 바인딩하는 것을 시뮬레이트하는 함수를 얻습니다. 원래 기능을 그대로 유지
Jason Pratt가 게시 한 내용이 정확합니다.
>>> class Test(object):
... def a(self):
... pass
...
>>> def b(self):
... pass
...
>>> Test.b = b
>>> type(b)
<type 'function'>
>>> type(Test.a)
<type 'instancemethod'>
>>> type(Test.b)
<type 'instancemethod'>
보시다시피, 파이썬은 b ()를 a ()와 다른 것으로 간주하지 않습니다. 파이썬에서 모든 메소드는 함수가되는 변수 일뿐입니다.
Test
가 아닌 인스턴스를 패치하고 있습니다.
from types import MethodType
def method(self):
print 'hi!'
setattr( targetObj, method.__name__, MethodType(method, targetObj, type(method)) )
이것으로, 당신은 자기 포인터를 사용할 수 있습니다
MethodType
호출하고 함수가 인스턴스를 생성하도록 합니다 . 에 바인딩 된 바인딩 된 메서드를 생성합니다 .barFighters.__get__(a)
barFighters
a