Menambahkan Metode ke Instance Obyek yang Ada

643

Saya telah membaca bahwa adalah mungkin untuk menambahkan metode ke objek yang sudah ada (yaitu, tidak dalam definisi kelas) dengan Python.

Saya mengerti bahwa tidak selalu baik untuk melakukannya. Tetapi bagaimana mungkin orang melakukan ini?

akdom
sumber

Jawaban:

921

Dalam Python, ada perbedaan antara fungsi dan metode terikat.

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

Metode terikat telah "terikat" (bagaimana deskriptif) ke sebuah instance, dan instance itu akan dilewatkan sebagai argumen pertama setiap kali metode dipanggil.

Callable yang merupakan atribut dari kelas (sebagai lawan dari instance) masih tidak terikat, jadi Anda dapat memodifikasi definisi kelas kapan pun Anda inginkan:

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

Instance yang didefinisikan sebelumnya diperbarui juga (selama mereka belum menimpa atribut itu sendiri):

>>> a.fooFighters()
fooFighters

Masalahnya muncul ketika Anda ingin melampirkan metode ke satu contoh:

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

Fungsi tidak terikat secara otomatis ketika dilampirkan langsung ke instance:

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

Untuk mengikatnya, kita bisa menggunakan fungsi MethodType di modul types :

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

Kali ini contoh lain dari kelas belum terpengaruh:

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

Informasi lebih lanjut dapat ditemukan dengan membaca tentang deskriptor dan pemrograman metaclass .

Jason Pratt
sumber
65
Daripada membuat secara manual MethodType, aktifkan protokol deskriptor secara manual dan mintalah fungsi menghasilkan instance Anda: barFighters.__get__(a)menghasilkan metode barFightersterikat untuk terikat a.
Martijn Pieters
2
@ MartijnPieters keuntungan menggunakan descriptor protocolvs membuat MethodTypeselain mungkin menjadi sedikit lebih mudah dibaca.
EndermanAPM
17
@EndermanAPM: Beberapa: lebih mungkin untuk terus bekerja persis sama dengan apa yang mengakses atribut pada instance. Ini akan bekerja untuk classmethoddan staticmethoddeskriptor lainnya juga. Ini menghindari mengacaukan namespace dengan impor lain.
Martijn Pieters
34
Kode lengkap untuk pendekatan deskriptor yang disarankan adalaha.barFighters = barFighters.__get__(a)
eqzx
98

Modul baru sudah ditinggalkan sejak python 2.6 dan dihapus di 3.0, gunakan tipe

lihat http://docs.python.org/library/new.html

Dalam contoh di bawah ini, saya sengaja menghapus nilai balik dari patch_me()fungsi. Saya pikir memberikan nilai kembali mungkin membuat orang percaya bahwa patch mengembalikan objek baru, yang tidak benar - itu memodifikasi yang masuk. Mungkin ini dapat memfasilitasi penggunaan monkeypatching yang lebih disiplin.

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'>
Evgeny
sumber
1
Bagaimana ini bekerja jika metode yang ditambahkan perlu merujuk ke diri sendiri? Percobaan pertama saya mengarah ke kesalahan sintaksis, tetapi menambahkan diri dalam definisi metode tampaknya tidak berhasil. impor tipe kelas A (objek): # tetapi tampaknya berfungsi untuk objek gaya lama juga ax = 'ax' pass def patch_me (target): metode def (target, x): print (self.ax) print ("x =" , x) cetak ("dipanggil dari", target) target.method = types.MethodType (metode, target) # tambahkan lebih banyak jika diperlukan cetak a = A () (a.ax)
WesR
85

Pendahuluan - catatan tentang kompatibilitas: jawaban lain hanya dapat bekerja di Python 2 - jawaban ini harus bekerja dengan baik di Python 2 dan 3. Jika menulis Python 3 saja, Anda mungkin meninggalkan pewaris secara eksplisit dari object, tetapi jika kode tetap sama .

Menambahkan Metode ke Instance Obyek yang Ada

Saya telah membaca bahwa adalah mungkin untuk menambahkan metode ke objek yang ada (misalnya tidak dalam definisi kelas) dengan Python.

Saya mengerti bahwa itu tidak selalu merupakan keputusan yang baik untuk melakukannya. Tapi, bagaimana mungkin orang melakukan ini?

Ya, itu mungkin - Tetapi tidak direkomendasikan

Saya tidak merekomendasikan ini. Ini ide yang buruk. Jangan lakukan itu.

Berikut beberapa alasan:

  • Anda akan menambahkan objek terikat ke setiap instance yang Anda lakukan. Jika Anda sering melakukan ini, Anda mungkin akan menghabiskan banyak memori. Metode terikat biasanya hanya dibuat untuk durasi singkat panggilan mereka, dan kemudian tidak ada lagi ketika sampah dikumpulkan secara otomatis. Jika Anda melakukan ini secara manual, Anda akan memiliki nama yang mengikat referensi metode terikat - yang akan mencegah pengumpulan sampah pada penggunaan.
  • Contoh objek dari tipe yang diberikan umumnya memiliki metode pada semua objek dari tipe itu. Jika Anda menambahkan metode di tempat lain, beberapa contoh akan memiliki metode tersebut dan yang lain tidak. Programmer tidak akan mengharapkan ini, dan Anda berisiko melanggar aturan yang paling tidak mengejutkan .
  • Karena ada alasan lain yang sangat bagus untuk tidak melakukan ini, Anda juga akan memberi reputasi buruk pada diri sendiri jika melakukannya.

Jadi, saya sarankan Anda tidak melakukan ini kecuali Anda memiliki alasan yang sangat bagus. Jauh lebih baik untuk mendefinisikan metode yang benar dalam definisi kelas atau kurang disukai untuk menambal monyet kelas secara langsung, seperti ini:

Foo.sample_method = sample_method

Karena ini bersifat instruktif, saya akan menunjukkan kepada Anda beberapa cara untuk melakukan ini.

Bagaimana itu bisa dilakukan

Ini beberapa kode pengaturan. Kami membutuhkan definisi kelas. Bisa diimpor, tetapi sebenarnya tidak masalah.

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

Buat sebuah instance:

foo = Foo()

Buat metode untuk ditambahkan:

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

Metode sia-sia (0) - gunakan metode deskriptor, __get__

Pencarian putus-putus pada fungsi memanggil __get__metode fungsi dengan instance, mengikat objek ke metode dan dengan demikian menciptakan "metode terikat."

foo.sample_method = sample_method.__get__(foo)

dan sekarang:

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

Metode satu - jenis. Tipe Metode

Pertama, impor jenis, dari mana kita akan mendapatkan konstruktor metode:

import types

Sekarang kita tambahkan metode ke instance. Untuk melakukan ini, kita memerlukan konstruktor MethodType dari typesmodul (yang kita impor di atas).

Tanda tangan argumen untuk types. Tipe metode adalah (function, instance, class):

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

dan penggunaan:

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

Metode dua: pengikatan leksikal

Pertama, kita membuat fungsi wrapper yang mengikat metode ke instance:

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

pemakaian:

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

Metode tiga: functools.partial

Fungsi parsial menerapkan argumen pertama pada suatu fungsi (dan argumen kata kunci opsional), dan kemudian dapat dipanggil dengan argumen yang tersisa (dan mengesampingkan argumen kata kunci). Jadi:

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

Ini masuk akal ketika Anda menganggap bahwa metode terikat adalah fungsi parsial dari instance.

Tidak terikat fungsi sebagai atribut objek - mengapa ini tidak berfungsi:

Jika kita mencoba menambahkan sample_method dengan cara yang sama seperti kita menambahkannya ke kelas, itu tidak terikat dari instance, dan tidak menganggap diri implisit sebagai argumen pertama.

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

Kita dapat membuat fungsi tidak terikat berfungsi dengan secara eksplisit melewatkan instance (atau apa pun, karena metode ini tidak benar-benar menggunakan selfvariabel argumen), tetapi itu tidak akan konsisten dengan tanda tangan yang diharapkan dari instance lain (jika kita menambal-monyet contoh ini):

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

Kesimpulan

Anda sekarang tahu beberapa cara Anda bisa melakukan ini, tetapi dengan sungguh-sungguh - jangan lakukan ini.

Aaron Hall
sumber
1
The Penolakan adalah apa yang saya bertanya-tanya tentang. Definisi metode hanyalah fungsi yang bersarang di dalam definisi kelas.
Atcold
1
@Atcold Saya telah memperluas alasan untuk menghindari melakukan ini dalam pendahuluan.
Aaron Hall
The __get__Metode juga perlu kelas sebagai parameter berikutnya: sample_method.__get__(foo, Foo).
Aidas Bendoraitis
2
@AidasBendoraitis Saya tidak akan mengatakan itu "perlu" itu, ini merupakan parameter opsional yang diberikan ketika menerapkan protokol descriptor - tetapi fungsi python tidak menggunakan argumen: github.com/python/cpython/blob/master/Objects/funcobject .c # L581
Aaron Hall
Komentar saya didasarkan pada referensi ini: python-reference.readthedocs.io/en/latest/docs/dunderdsc/... Apa yang sekarang saya lihat dari dokumen resmi, opsional: docs.python.org/3/howto/descriptor.html# descriptor-protocol
Aidas Bendoraitis
35

Saya pikir jawaban di atas melewatkan poin kunci.

Mari kita kelas dengan metode:

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

Sekarang, mari kita bermain dengannya di ipython:

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

Ok, jadi m () entah bagaimana menjadi metode terikat dari A . Tapi benarkah seperti itu?

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

Ternyata m () hanya fungsi, referensi yang ditambahkan ke A kamus kelas - tidak ada sihir. Lalu mengapa Am memberi kami metode yang tidak terikat? Itu karena titik tidak diterjemahkan ke pencarian kamus sederhana. Secara de facto panggilan dari kelas A.__ __.__ 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

Sekarang, saya tidak yakin dari atas kepala saya mengapa baris terakhir dicetak dua kali, tetapi masih jelas apa yang terjadi di sana.

Sekarang, apa yang dilakukan oleh __getattribute__ default adalah ia memeriksa apakah atributnya disebut descriptor atau tidak, yaitu jika ia mengimplementasikan metode __get__ khusus. Jika mengimplementasikan metode itu, maka apa yang dikembalikan adalah hasil dari memanggil metode __get__ itu. Kembali ke versi pertama dari kelas A kami , inilah yang kami miliki:

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

Dan karena fungsi Python mengimplementasikan protokol deskriptor, jika mereka dipanggil atas nama objek, mereka mengikat diri ke objek itu dalam metode __get__ mereka.

Oke, jadi bagaimana cara menambahkan metode ke objek yang ada? Dengan asumsi Anda tidak keberatan menambal kelas, itu sesederhana:

B.m = m

Kemudian Bm "menjadi" metode yang tidak terikat, berkat sihir deskriptor.

Dan jika Anda ingin menambahkan metode hanya ke objek tunggal, maka Anda harus meniru mesin sendiri, dengan menggunakan types.MethodType:

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

Ngomong-ngomong:

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'>
Tomasz Zieliński
sumber
20

Di Python, tambalan monyet biasanya berfungsi dengan menimpa kelas atau fungsi dengan tanda tangan Anda. Di bawah ini adalah contoh dari Zope Wiki :

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

Kode itu akan menimpa / membuat metode yang disebut berbicara di kelas. Dalam posting terbaru Jeff Atwood tentang penambalan monyet . Dia menunjukkan contoh dalam C # 3.0 yang merupakan bahasa saat ini yang saya gunakan untuk bekerja.

John Downey
sumber
6
Tetapi itu memengaruhi semua instance kelas, bukan hanya satu.
glglgl
14

Anda bisa menggunakan lambda untuk mengikat metode ke instance:

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

Keluaran:

This is instance string
Evgeny Prokurat
sumber
9

Setidaknya ada dua cara untuk melampirkan metode ke instance tanpa 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>>

Tautan yang berguna:
model data - memanggil deskriptor
Descriptor Panduan HowTo - memanggil deskriptor

ndpu
sumber
7

Apa yang Anda cari adalah setattrsaya percaya. Gunakan ini untuk mengatur atribut pada objek.

>>> 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>
HS.
sumber
8
Ini menambal kelas A, bukan instance a.
Ethan Furman
5
Apakah ada alasan untuk menggunakannya, setattr(A,'printme',printme)bukan hanya A.printme = printme?
Tobias Kienzler
1
Masuk akal jika seseorang membangun nama metode saat runtime.
rr-
6

Karena pertanyaan ini menanyakan versi non-Python, inilah JavaScript:

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

Mengkonsolidasikan jawaban Jason Pratt dan komunitas wiki, dengan melihat hasil dari berbagai metode pengikatan:

Terutama perhatikan bagaimana menambahkan fungsi penjilidan sebagai metode kelas berfungsi , tetapi cakupan referensi salah.

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

Secara pribadi, saya lebih suka rute fungsi ADDMETHOD eksternal, karena memungkinkan saya untuk secara dinamis menetapkan nama metode baru di dalam iterator juga.

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>>
Nisan.H
sumber
addmethodditulis ulang dengan cara berikut def addmethod(self, method, name): self.__dict__[name] = types.MethodType( method, self )memecahkan masalah
Antony Hatchkins
5

Ini sebenarnya merupakan tambahan untuk jawaban "Jason Pratt"

Meskipun jawaban Jason berhasil, itu hanya berfungsi jika seseorang ingin menambahkan fungsi ke kelas. Itu tidak berfungsi untuk saya ketika saya mencoba memuat ulang metode yang sudah ada dari file kode sumber .py.

Butuh waktu lama bagi saya untuk menemukan solusi, tetapi triknya tampak sederhana ... 1. mengimpor kode dari file kode sumber 2.dan memaksa memuat ulang jenis penggunaan yang ketiga. FunctionType (...) untuk mengonversi Metode yang diimpor dan terikat ke fungsi Anda juga dapat meneruskan variabel global saat ini, karena metode yang dimuat ulang akan berada dalam ruang nama yang berbeda 4. Sekarang Anda dapat melanjutkan seperti yang disarankan oleh "Jason Pratt" menggunakan types. Methype ... )

Contoh:

# 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
Maks
sumber
3

Jika itu bisa membantu, saya baru-baru ini merilis perpustakaan Python bernama Gorilla untuk membuat proses penambalan monyet lebih nyaman.

Menggunakan fungsi needle()untuk menambal modul bernama guineapigberjalan sebagai berikut:

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

Tetapi juga menangani kasus penggunaan yang lebih menarik seperti yang ditunjukkan dalam FAQ dari dokumentasi .

Kode ini tersedia di GitHub .

ChristopherC
sumber
3

Pertanyaan ini dibuka bertahun-tahun yang lalu, tapi hei, ada cara mudah untuk mensimulasikan pengikatan fungsi ke instance kelas menggunakan dekorator:

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

Di sana, ketika Anda meneruskan fungsi dan instance ke pengikat binder, itu akan membuat fungsi baru, dengan objek kode yang sama dengan yang pertama. Kemudian, instance kelas yang diberikan disimpan dalam atribut fungsi yang baru dibuat. Dekorator mengembalikan fungsi (ketiga) yang memanggil fungsi yang disalin secara otomatis, memberikan instance sebagai parameter pertama.

Kesimpulannya Anda mendapatkan fungsi mensimulasikan mengikat ke instance kelas. Membiarkan fungsi asli tidak berubah.

berbaring
sumber
2

Apa yang diposting Jason Pratt benar.

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

Seperti yang Anda lihat, Python tidak menganggap b () berbeda dari a (). Dalam Python semua metode hanyalah variabel yang merupakan fungsi.

Akumulasi
sumber
7
Anda menambal kelas Test, bukan turunannya.
Ethan Furman
Anda menambahkan metode ke kelas, bukan instance objek.
TomSawyer
2

Saya merasa aneh bahwa tidak ada yang menyebutkan bahwa semua metode yang tercantum di atas membuat referensi siklus antara metode yang ditambahkan dan instance, menyebabkan objek menjadi persisten hingga pengumpulan sampah. Ada trik lama menambahkan deskripsi dengan memperluas kelas objek:

def addmethod(obj, name, func):
    klass = obj.__class__
    subclass = type(klass.__name__, (klass,), {})
    setattr(subclass, name, func)
    obj.__class__ = subclass
Yu Feng
sumber
2
from types import MethodType

def method(self):
   print 'hi!'


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

Dengan ini, Anda dapat menggunakan penunjuk diri

Arturo Morales Rangel
sumber