기존 객체 인스턴스에 메소드 추가


642

파이썬에서 기존 객체 (예 : 클래스 정의가 아닌)에 메소드를 추가 할 수 있다는 것을 읽었습니다.

그렇게하는 것이 항상 좋은 것은 아니라는 것을 알고 있습니다. 그러나 어떻게 이것을 할 수 있습니까?

답변:


921

파이썬에서는 함수와 바운드 메소드간에 차이가 있습니다.

>>> 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'

디스크립터메타 클래스 프로그래밍대한 자세한 내용은 자세한 내용을 참조하십시오 .


65
를 수동으로 생성하는 대신 설명자 프로토콜을 수동으로 MethodType호출하고 함수가 인스턴스를 생성하도록 합니다 . 에 바인딩 된 바인딩 된 메서드를 생성합니다 . barFighters.__get__(a)barFightersa
Martijn Pieters

2
@MartijnPieters 는 약간 더 읽기 쉬운 것을 제외 하고 descriptor protocolvs 를 사용하는 것의 이점을 제공합니다 MethodType.
EndermanAPM

17
@ EndermanAPM : 몇 가지 : 인스턴스의 속성에 액세스하는 것과 정확히 동일하게 작동 할 가능성이 높습니다. 그것은 작동합니다 classmethodstaticmethod다른 설명도. 또 다른 가져 오기로 네임 스페이스를 복잡하게 만들지 않습니다.
Martijn Pieters

34
제안 된 디스크립터 접근법의 전체 코드는 다음과 같습니다.a.barFighters = barFighters.__get__(a)
eqzx

98

새로운 모듈 은 파이썬 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'>

1
추가되는 방법이 자체를 참조해야하는 경우 어떻게 작동합니까? 첫 번째 시도는 구문 오류로 이어지지 만 메소드 정의에 자체를 추가하면 작동하지 않는 것 같습니다. 가져 오기 유형 class A (object) : # 그러나 이전 스타일 객체에서도 작동하는 것 같습니다. ax = 'ax'pass def patch_me (target) : def method (target, x) : print (self.ax) print ( "x =" , x) print ( "from from", target) target.method = types.MethodType (method, target) # 필요한 경우 더 추가 a = A () print (a.ax)
WesR

85

서문-호환성에 대한 참고 사항 : 다른 답변은 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)

메소드 naught (0)-디스크립터 메소드를 사용하십시오. __get__

함수에 대한 점선 조회 __get__는 인스턴스와 함께 함수 의 메서드를 호출하여 개체를 메서드에 바인딩하여 "바운드 메서드"를 만듭니다.

foo.sample_method = sample_method.__get__(foo)

그리고 지금:

>>> foo.sample_method(1,2)
3

방법 1-types.MethodType

먼저 가져 오기 유형을 통해 메소드 생성자를 가져옵니다.

import types

이제 메소드를 인스턴스에 추가합니다. 이를 위해 types모듈 에서 MethodType 생성자가 필요합니다 (위에서 가져옴).

types.MethodType의 인수 서명은 (function, instance, class)다음과 같습니다.

foo.sample_method = types.MethodType(sample_method, foo, Foo)

그리고 사용법 :

>>> foo.sample_method(1,2)
3

방법 2 : 어휘 바인딩

먼저 메서드를 인스턴스에 바인딩하는 래퍼 함수를 ​​만듭니다.

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

방법 3 : functools.partial

부분 함수는 첫 번째 인수를 함수 (및 선택적으로 키워드 인수)에 적용하고 나중에 나머지 인수와 함께 키워드 인수를 재정 의하여 호출 할 수 있습니다. 그러므로:

>>> 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

결론

당신은 지금 당신 이 이것을 할 있는 몇 가지 방법을 알고 있지만, 진지하게-이것을하지 마십시오.


1
면책 조항 I에 대해 궁금 해서요 것입니다. 메소드 정의는 단순히 클래스 정의 내에 중첩 된 함수입니다.
Atcold

1
@Atcold 소개에서 이것을 피하는 이유를 확장했습니다.
Aaron Hall

__get__메소드에는 다음 매개 변수로 클래스가 필요합니다 sample_method.__get__(foo, Foo).
Aidas Bendoraitis

2
@AidasBendoraitis "필요하다"고 말하지는 않지만 설명자 프로토콜을 적용 할 때 제공되는 선택적 매개 변수입니다. 그러나 Python 함수는 github.com/python/cpython/blob/master/Objects/funcobject
Aaron Hall

내 의견은이 참조를 기반으로했습니다 : python-reference.readthedocs.io/en/latest/docs/dunderdsc/… 이제 공식 문서에서 볼 수있는 것은 선택 사항입니다 : docs.python.org/3/howto/descriptor.html# 디스크립터 프로토콜
Aidas Bendoraitis

35

위의 답변이 요점을 놓쳤다 고 생각합니다.

메소드가있는 클래스를 보자.

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'>

19

파이썬에서 원숭이 패치는 일반적으로 자신의 클래스 또는 함수 서명을 덮어 쓰면 작동합니다. 다음은 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의 예를 보여줍니다.


6
그러나 그것은 수업이 아닌 수업의 모든 사례에 영향을 미칩니다 .
glglgl

14

람다를 사용하여 메소드를 인스턴스에 바인딩 할 수 있습니다.

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

9

메소드없이 인스턴스에 메소드를 첨부하는 방법은 두 가지 이상 있습니다 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>>

유용한 링크 :
데이터 모델-디스크립터 호출 설명자
사용법 안내서-디스크립터 호출


7

당신이 찾고있는 것은 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>

8
이것은 A인스턴스가 아닌 클래스를 패치하고 a있습니다.
Ethan Furman

5
setattr(A,'printme',printme)단순히 대신 사용해야 할 이유가 A.printme = printme있습니까?
Tobias Kienzler

1
런타임에 메소드 이름을 구성하는 것이 좋습니다.
rr-

6

이 질문은 비 Python 버전을 요청했기 때문에 JavaScript는 다음과 같습니다.

a.methodname = function () { console.log("Yay, a new method!") }

5

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 )하면 문제가 해결됩니다
Antony Hatchkins

5

이것은 실제로 "Jason Pratt"의 답변에 대한 애드온입니다

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

3

도움이 될 수 있다면 최근 고릴라라는 파이썬 라이브러리를 출시하여 원숭이 패치 프로세스를보다 편리하게 만들었습니다.

함수 needle()를 사용하여 명명 된 모듈을 패치하면 guineapig다음과 같습니다.

import gorilla
import guineapig
@gorilla.patch(guineapig)
def needle():
    print("awesome")

같이 그러나 그것은 또한 더 흥미로운 사용 사례를 돌봐 질문 으로부터 문서 .

코드는 GitHub에서 사용할 수 있습니다 .


3

이 질문은 몇 년 전에 시작되었지만 데코레이터를 사용하여 클래스 인스턴스에 함수의 바인딩을 시뮬레이션하는 쉬운 방법이 있습니다.

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 ()

거기에서 함수와 인스턴스를 바인더 데코레이터에 전달하면 첫 번째 함수와 동일한 코드 객체를 사용하여 새 함수가 생성됩니다. 그런 다음 지정된 클래스 인스턴스가 새로 작성된 함수의 속성에 저장됩니다. 데코레이터는 복사 된 함수를 자동으로 호출하는 (세번째) 함수를 반환하여 인스턴스를 첫 번째 매개 변수로 제공합니다.

결론적으로 클래스 인스턴스에 바인딩하는 것을 시뮬레이트하는 함수를 얻습니다. 원래 기능을 그대로 유지


2

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 ()와 다른 것으로 간주하지 않습니다. 파이썬에서 모든 메소드는 함수가되는 변수 일뿐입니다.


7
클래스 Test가 아닌 인스턴스를 패치하고 있습니다.
Ethan Furman

객체 인스턴스가 아닌 클래스에 메소드를 추가하고 있습니다.
TomSawyer 2009 년

2

위에서 언급 한 모든 메소드가 추가 된 메소드와 인스턴스 사이에 사이클 참조를 생성하여 가비지 수집까지 객체가 지속된다고 언급 한 사람은 아무도 없습니다. 객체의 클래스를 확장하여 설명자를 추가하는 오래된 트릭이있었습니다.

def addmethod(obj, name, func):
    klass = obj.__class__
    subclass = type(klass.__name__, (klass,), {})
    setattr(subclass, name, func)
    obj.__class__ = subclass

2
from types import MethodType

def method(self):
   print 'hi!'


setattr( targetObj, method.__name__, MethodType(method, targetObj, type(method)) )

이것으로, 당신은 자기 포인터를 사용할 수 있습니다

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.