Thêm một phương thức vào một đối tượng hiện có


643

Tôi đã đọc rằng có thể thêm một phương thức vào một đối tượng hiện có (nghĩa là không có trong định nghĩa lớp) trong Python.

Tôi hiểu rằng không phải lúc nào cũng tốt để làm như vậy. Nhưng làm thế nào một người có thể làm điều này?

Câu trả lời:


921

Trong Python, có một sự khác biệt giữa các hàm và các phương thức ràng buộc.

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

Các phương thức ràng buộc đã được "ràng buộc" (mô tả như thế nào) với một thể hiện và thể hiện đó sẽ được truyền làm đối số đầu tiên bất cứ khi nào phương thức được gọi.

Mặc dù vậy, các hàm gọi là thuộc tính của một lớp (trái ngược với một thể hiện) vẫn không bị ràng buộc, vì vậy bạn có thể sửa đổi định nghĩa lớp bất cứ khi nào bạn muốn:

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

Các trường hợp được xác định trước đó cũng được cập nhật (miễn là chúng không ghi đè lên thuộc tính):

>>> a.fooFighters()
fooFighters

Vấn đề xảy ra khi bạn muốn đính kèm một phương thức vào một thể hiện duy nhất:

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

Hàm không tự động bị ràng buộc khi được gắn trực tiếp vào một thể hiện:

>>> a.barFighters
<function barFighters at 0x00A98EF0>

Để liên kết nó, chúng ta có thể sử dụng hàm MethodType trong mô đun kiểu :

>>> import types
>>> a.barFighters = types.MethodType( barFighters, a )
>>> a.barFighters
<bound method ?.barFighters of <__main__.A instance at 0x00A9BC88>>
>>> a.barFighters()
barFighters

Lần này các trường hợp khác của lớp không bị ảnh hưởng:

>>> a2.barFighters()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: A instance has no attribute 'barFighters'

Thông tin thêm có thể được tìm thấy bằng cách đọc về mô tảlập trình siêu dữ liệu .


65
Thay vì tự tạo một MethodType, hãy gọi giao thức mô tả theo cách thủ công và có chức năng tạo ra cá thể của bạn: barFighters.__get__(a)tạo ra một phương thức barFightersràng buộc cho ràng buộc a.
Martijn Pieters

2
@MartijnPieters bất kỳ lợi thế của việc sử dụng descriptor protocolvs tạo ra một MethodTypebên có thể dễ đọc hơn một chút.
EndermanAPM

17
@EndermanAPM: Một số: có nhiều khả năng tiếp tục hoạt động chính xác giống như những gì truy cập thuộc tính trên một cá thể. Nó sẽ làm việc cho classmethodstaticmethodvà mô tả khác nữa. Nó tránh làm lộn xộn không gian tên với một lần nhập khác.
Martijn Pieters

34
Mã đầy đủ cho phương pháp mô tả được đề xuất làa.barFighters = barFighters.__get__(a)
eqzx

98

Mô-đun mới không được dùng nữa từ python 2.6 và bị xóa trong 3.0, sử dụng các loại

xem http://docs.python.org/l Library / new.html

Trong ví dụ dưới đây, tôi đã cố tình xóa giá trị trả về khỏi patch_me()hàm. Tôi nghĩ rằng việc đưa ra giá trị trả về có thể khiến người ta tin rằng bản vá trả về một đối tượng mới, điều đó không đúng - nó sửa đổi đối tượng đến. Có lẽ điều này có thể tạo điều kiện cho việc sử dụng con khỉ có kỷ luật hơn.

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
Làm thế nào điều này sẽ làm việc nếu phương pháp được thêm vào cần phải tự tham khảo? Lần thử đầu tiên của tôi dẫn đến một lỗi cú pháp, nhưng việc tự thêm vào định nghĩa của phương thức dường như không hoạt động. nhập loại lớp A (đối tượng): # nhưng dường như hoạt động đối với các đối tượng kiểu cũ quá ax = 'ax' pass def patch_me (đích): phương thức def (đích, x): print (self.ax) print ("x =" , x) print ("được gọi từ", target) target.method = type.MethodType (phương thức, đích) #add more nếu cần a = A () print (a.ax)
WesR

85

Lời nói đầu - một lưu ý về khả năng tương thích: các câu trả lời khác chỉ có thể hoạt động trong Python 2 - câu trả lời này sẽ hoạt động hoàn hảo trong Python 2 và 3. Nếu chỉ viết Python 3, bạn có thể bỏ qua kế thừa rõ ràng object, nhưng nếu không thì mã vẫn giữ nguyên .

Thêm một phương thức vào một đối tượng hiện có

Tôi đã đọc rằng có thể thêm một phương thức vào một đối tượng hiện có (ví dụ không phải trong định nghĩa lớp) trong Python.

Tôi hiểu rằng không phải lúc nào cũng là một quyết định tốt để làm như vậy. Nhưng, làm thế nào người ta có thể làm điều này?

Có, điều đó là có thể - Nhưng không nên

Tôi không khuyên bạn điều này. Đây là một ý tưởng tồi. Đừng làm điều đó.

Đây là một vài lý do:

  • Bạn sẽ thêm một đối tượng ràng buộc cho mọi trường hợp bạn làm điều này. Nếu bạn làm điều này nhiều, có lẽ bạn sẽ lãng phí rất nhiều bộ nhớ. Các phương thức ràng buộc thường chỉ được tạo trong thời gian ngắn của cuộc gọi và sau đó chúng sẽ ngừng tồn tại khi tự động thu gom rác. Nếu bạn thực hiện việc này một cách thủ công, bạn sẽ có một ràng buộc tên tham chiếu phương thức bị ràng buộc - điều này sẽ ngăn việc thu gom rác của nó khi sử dụng.
  • Các thể hiện đối tượng của một kiểu nhất định thường có các phương thức của nó trên tất cả các đối tượng của kiểu đó. Nếu bạn thêm các phương thức ở nơi khác, một số trường hợp sẽ có các phương thức đó và các phương thức khác thì không. Các lập trình viên sẽ không mong đợi điều này và bạn có nguy cơ vi phạm quy tắc ít bất ngờ nhất .
  • Vì có những lý do thực sự tốt khác để không làm điều này, bạn cũng sẽ tạo cho mình một danh tiếng kém nếu bạn làm điều đó.

Vì vậy, tôi khuyên bạn không nên làm điều này trừ khi bạn có lý do thực sự tốt. Sẽ tốt hơn nhiều khi định nghĩa phương thức đúng trong định nghĩa lớp hoặc ít nhất là tốt hơn để vá trực tiếp lớp, như sau:

Foo.sample_method = sample_method

Vì đó là hướng dẫn, tuy nhiên, tôi sẽ chỉ cho bạn một số cách để làm điều này.

Làm thế nào nó có thể được thực hiện

Đây là một số mã thiết lập. Chúng ta cần một định nghĩa lớp. Nó có thể được nhập khẩu, nhưng nó thực sự không quan trọng.

class Foo(object):
    '''An empty class to demonstrate adding a method to an instance'''

Tạo một ví dụ:

foo = Foo()

Tạo một phương thức để thêm vào nó:

def sample_method(self, bar, baz):
    print(bar + baz)

Phương thức vô hiệu (0) - sử dụng phương thức mô tả, __get__

Tra cứu chấm trên các hàm gọi __get__phương thức của hàm với thể hiện, liên kết đối tượng với phương thức và do đó tạo ra "phương thức ràng buộc".

foo.sample_method = sample_method.__get__(foo)

và bây giờ:

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

Phương thức một - loại.MethodType

Đầu tiên, nhập các loại, từ đó chúng ta sẽ có hàm tạo phương thức:

import types

Bây giờ chúng ta thêm phương thức vào ví dụ. Để làm điều này, chúng tôi yêu cầu hàm tạo Phương thức Kiểu từ typesmô-đun (mà chúng tôi đã nhập ở trên).

Chữ ký đối số cho các loại.MethodType là (function, instance, class):

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

và cách sử dụng:

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

Phương pháp hai: ràng buộc từ vựng

Đầu tiên, chúng ta tạo một hàm bao bọc liên kết phương thức với thể hiện:

def bind(instance, method):
    def binding_scope_fn(*args, **kwargs): 
        return method(instance, *args, **kwargs)
    return binding_scope_fn

sử dụng:

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

Phương pháp ba: funcools.partial

Hàm một phần áp dụng (các) đối số đầu tiên cho một hàm (và các đối số từ khóa tùy chọn) và sau đó có thể được gọi với các đối số còn lại (và ghi đè các đối số từ khóa). Như vậy:

>>> from functools import partial
>>> foo.sample_method = partial(sample_method, foo)
>>> foo.sample_method(1,2)
3    

Điều này có ý nghĩa khi bạn xem xét rằng các phương thức ràng buộc là các hàm một phần của thể hiện.

Hàm không liên kết như một thuộc tính đối tượng - tại sao điều này không hoạt động:

Nếu chúng ta cố gắng thêm sample_method theo cùng một cách như chúng ta có thể thêm nó vào lớp, thì nó không bị ràng buộc từ thể hiện và không lấy bản thân ẩn làm đối số đầu tiên.

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

Chúng ta có thể làm cho hàm không liên kết hoạt động bằng cách chuyển rõ ràng thể hiện (hoặc bất cứ điều gì, vì phương thức này không thực sự sử dụng selfbiến đối số), nhưng nó sẽ không phù hợp với chữ ký dự kiến ​​của các trường hợp khác (nếu chúng ta đang vá khỉ ví dụ này):

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

Phần kết luận

Bây giờ bạn biết một số cách bạn có thể làm điều này, nhưng thực sự nghiêm túc - đừng làm điều này.


1
Các Tuyên bố từ chối là những gì tôi đã tự hỏi về. Định nghĩa phương thức là các hàm đơn giản được lồng trong định nghĩa lớp.
Vào

1
@Atcold Tôi đã mở rộng lý do để tránh làm điều này trong phần giới thiệu.
Aaron Hall

Các __get__phương pháp cũng cần lớp như tham số tiếp theo: sample_method.__get__(foo, Foo).
Aidas Bendora viêm

2
@AidasBendora viêm Tôi không nói nó "cần" nó, đó là một tham số tùy chọn được cung cấp khi áp dụng giao thức mô tả - nhưng các hàm python không sử dụng đối số: github.com/python/cpython/blob/master/Objects/funcobject .c # L581
Aaron Hall

Nhận xét của tôi dựa trên tài liệu tham khảo này: python-reference.readthedocs.io/en/latest/docs/dunderdsc/ trộm Những gì tôi thấy bây giờ từ các tài liệu chính thức, đó là tùy chọn: docs.python.org/3/howto/descriptor.html# mô tả-giao thức
Aidas Bendora viêm

35

Tôi nghĩ rằng các câu trả lời trên đã bỏ lỡ điểm quan trọng.

Chúng ta hãy có một lớp học với một phương thức:

class A(object):
    def m(self):
        pass

Bây giờ, hãy chơi với nó trong ipython:

In [2]: A.m
Out[2]: <unbound method A.m>

Ok, vậy m () bằng cách nào đó sẽ trở thành một phương pháp không ràng buộc của Một . Nhưng nó có thực sự như vậy không?

In [5]: A.__dict__['m']
Out[5]: <function m at 0xa66b8b4>

Hóa ra m () chỉ là một hàm, tham chiếu được thêm vào từ điển lớp A - không có phép thuật. Vậy thì tại sao Am lại cho chúng ta một phương pháp không ràng buộc? Đó là vì dấu chấm không được dịch sang tra cứu từ điển đơn giản. Đây thực tế là một cuộc gọi của lớp A .__ __.__ getattribution __ (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

Bây giờ, tôi không chắc chắn ra khỏi đầu của mình tại sao dòng cuối cùng được in hai lần, nhưng vẫn rõ ràng những gì đang xảy ra ở đó.

Bây giờ, những gì __getattribution__ mặc định làm là nó kiểm tra xem thuộc tính có được gọi là mô tả hay không, tức là nếu nó thực hiện một phương thức __get__ đặc biệt. Nếu nó thực hiện phương thức đó, thì cái được trả về là kết quả của việc gọi phương thức __get__ đó. Quay trở lại phiên bản đầu tiên của lớp A của chúng tôi , đây là những gì chúng tôi có:

In [28]: A.__dict__['m'].__get__(None, A)
Out[28]: <unbound method A.m>

Và bởi vì các hàm Python thực hiện giao thức mô tả, nếu chúng được gọi thay mặt cho một đối tượng, chúng tự liên kết với đối tượng đó trong phương thức __get__ của chúng.

Ok, vậy làm thế nào để thêm một phương thức vào một đối tượng hiện có? Giả sử bạn không bận tâm đến việc vá lớp, nó đơn giản như:

B.m = m

Sau đó, Bm "trở thành" một phương thức không liên kết, nhờ vào phép thuật mô tả.

Và nếu bạn muốn thêm một phương thức chỉ vào một đối tượng, thì bạn phải tự mô phỏng máy móc bằng cách sử dụng các loại.MethodType:

b.m = types.MethodType(m, b)

Nhân tiện:

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

20

Trong Python patch patch thường hoạt động bằng cách ghi đè một lớp hoặc chữ ký hàm với chữ ký của riêng bạn. Dưới đây là một ví dụ từ Zope Wiki :

from SomeOtherProduct.SomeModule import SomeClass
def speak(self):
   return "ook ook eee eee eee!"
SomeClass.speak = speak

Mã đó sẽ ghi đè / tạo một phương thức gọi là speak trên lớp. Trong bài đăng gần đây của Jeff Atwood về việc vá khỉ . Anh ta cho thấy một ví dụ trong C # 3.0, ngôn ngữ hiện tại tôi sử dụng cho công việc.


6
Nhưng nó ảnh hưởng đến tất cả các trường hợp của lớp, không chỉ một.
glglgl

14

Bạn có thể sử dụng lambda để liên kết một phương thức với một thể hiện:

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

Đầu ra:

This is instance string

9

Có ít nhất hai cách để đính kèm một phương thức vào một cá thể mà không có 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>>

Liên kết hữu ích:
Mô hình dữ liệu - gọi mô tả
Mô tả Hướng dẫn cách làm - gọi mô tả


7

Những gì bạn đang tìm kiếm là setattrtôi tin tưởng. Sử dụng điều này để đặt một thuộc tính trên một đối tượng.

>>> 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
Đây là bản vá của lớp A, không phải là ví dụ a.
Ethan Furman

5
Có một lý do để sử dụng setattr(A,'printme',printme)thay vì đơn giản A.printme = printme?
Tobias Kienzler

1
Nó có ý nghĩa nếu một người xây dựng tên phương thức trong thời gian chạy.
rr-

6

Vì câu hỏi này yêu cầu các phiên bản không phải Python, nên đây là JavaScript:

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

5

Hợp nhất các câu trả lời wiki của Jason Pratt và cộng đồng, với cái nhìn về kết quả của các phương pháp ràng buộc khác nhau:

Đặc biệt lưu ý cách thêm chức năng liên kết như một phương thức lớp hoạt động , nhưng phạm vi tham chiếu không chính xác.

#!/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>>

Cá nhân, tôi thích tuyến chức năng ADDMETHOD bên ngoài, vì nó cho phép tôi tự động gán tên phương thức mới trong một trình vòng lặp.

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

addmethodviết lại theo cách sau đây def addmethod(self, method, name): self.__dict__[name] = types.MethodType( method, self )giải quyết vấn đề
Antony Hatchkins

5

Đây thực sự là một addon cho câu trả lời của "Jason Pratt"

Mặc dù câu trả lời của Jasons hoạt động, nó chỉ hoạt động nếu một người muốn thêm một hàm vào một lớp. Nó không hoạt động với tôi khi tôi cố tải lại một phương thức đã có từ tệp mã nguồn .py.

Tôi mất nhiều thời gian để tìm ra cách giải quyết, nhưng thủ thuật có vẻ đơn giản ... 1. nhập mã từ tệp mã nguồn 2. và buộc tải lại 3.rd sử dụng các loại.FeftType (...) để chuyển đổi phương thức được nhập và ràng buộc vào một hàm mà bạn cũng có thể truyền vào các biến toàn cục hiện tại, vì phương thức được tải lại sẽ ở một không gian tên khác 4. bây giờ bạn có thể tiếp tục như được đề xuất bởi "Jason Pratt" bằng cách sử dụng các loại.MethodType (... )

Thí dụ:

# 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

Nếu nó có thể giúp được gì, gần đây tôi đã phát hành một thư viện Python có tên Gorilla để làm cho quá trình vá khỉ thuận tiện hơn.

Sử dụng một chức năng needle()để vá một mô-đun có tên guineapignhư sau:

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

Nhưng nó cũng quan tâm đến các trường hợp sử dụng thú vị hơn như được nêu trong Câu hỏi thường gặp từ tài liệu .

Mã này có sẵn trên GitHub .


3

Câu hỏi này đã được mở từ nhiều năm trước, nhưng này, có một cách dễ dàng để mô phỏng sự ràng buộc của một chức năng với một thể hiện của lớp bằng cách sử dụng các trình trang trí:

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

Ở đó, khi bạn truyền hàm và thể hiện cho trình trang trí chất kết dính, nó sẽ tạo ra một hàm mới, với cùng một đối tượng mã như hàm đầu tiên. Sau đó, thể hiện đã cho của lớp được lưu trữ trong một thuộc tính của hàm vừa tạo. Trình trang trí trả về một hàm (thứ ba) tự động gọi hàm được sao chép, đưa ra thể hiện làm tham số đầu tiên.

Để kết luận, bạn nhận được một hàm mô phỏng nó liên kết với thể hiện của lớp. Để chức năng ban đầu không thay đổi.


2

Những gì Jason Pratt đăng là chính xác.

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

Như bạn có thể thấy, Python không xem xét b () bất kỳ khác với a (). Trong Python tất cả các phương thức chỉ là các biến xảy ra là các hàm.


7
Bạn đang vá lớp Test, không phải là một thể hiện của nó.
Ethan Furman

Bạn đang thêm phương thức vào một lớp, không phải là một thể hiện đối tượng.
TomSawyer

2

Tôi thấy lạ là không ai đề cập rằng tất cả các phương thức được liệt kê ở trên tạo ra một tham chiếu chu kỳ giữa phương thức được thêm vào và thể hiện, làm cho đối tượng bị tồn tại cho đến khi thu gom rác. Có một thủ thuật cũ khi thêm một mô tả bằng cách mở rộng lớp của đối tượng:

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

Với điều này, bạn có thể sử dụng con trỏ tự

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.