Apakah ada cara dengan Python untuk mengganti metode kelas pada tingkat instance? Sebagai contoh:
class Dog:
def bark(self):
print "WOOF"
boby = Dog()
boby.bark() # WOOF
# METHOD OVERRIDE
boby.bark() # WoOoOoF!!
Tolong jangan lakukan ini seperti yang ditunjukkan. Kode Anda menjadi tidak bisa dibaca saat Anda mencocokkan instance agar berbeda dari kelas.
Anda tidak dapat men-debug kode monkeypatched.
Ketika Anda menemukan bug di boby
dan print type(boby)
, Anda akan melihat bahwa (a) itu adalah Anjing, tetapi (b) karena alasan yang tidak jelas ia tidak menggonggong dengan benar. Ini mimpi buruk. Jangan lakukan itu.
Silakan lakukan ini sebagai gantinya.
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!!
Iya itu mungkin:
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"
sumber
funcType
dan mengapa itu perlu.funcType = types.MethodType
(setelah mengimportypes
) daripadafuncType = type(Dog.bark)
.__get__
fungsi untuk mengikatnya ke instance.Anda perlu menggunakan MethodType dari
types
modul. TujuannyaMethodType
adalah untuk menimpa metode tingkat instance (sehinggaself
dapat tersedia dalam metode yang ditimpa).Lihat contoh di bawah ini.
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!!
sumber
Untuk menjelaskan jawaban bagus @ codelogic, saya mengusulkan pendekatan yang lebih eksplisit. Ini adalah teknik yang sama dengan
.
operator untuk mengikat metode kelas saat Anda mengaksesnya sebagai atribut instance, kecuali bahwa metode Anda sebenarnya adalah fungsi yang ditentukan di luar kelas.Bekerja dengan kode @ codelogic, satu-satunya perbedaan adalah cara mengikat metode. Saya menggunakan fakta bahwa fungsi dan metode adalah deskriptor non-data dalam Python, dan menggunakan
__get__
metode tersebut. Perhatikan terutama bahwa yang asli dan penggantinya memiliki tanda tangan yang identik, artinya Anda dapat menulis penggantinya sebagai metode kelas lengkap, mengakses semua atribut instance melaluiself
.Dengan menetapkan metode terikat ke atribut instance, Anda telah membuat simulasi yang hampir lengkap untuk mengganti metode. Satu fitur praktis yang hilang adalah akses ke versi no-arg
super
, karena Anda tidak berada dalam definisi kelas. Hal lainnya adalah bahwa__name__
atribut metode terikat Anda tidak akan mengambil nama fungsi yang ditimpanya, seperti yang akan terjadi pada definisi kelas, tetapi Anda masih dapat menyetelnya secara manual. Perbedaan ketiga adalah bahwa metode Anda yang terikat secara manual adalah referensi atribut biasa yang kebetulan merupakan sebuah fungsi. The.
Operator tidak apa-apa tapi mengambil referensi itu. Sebaliknya, saat memanggil metode reguler dari sebuah instance, proses binding membuat metode terikat baru setiap saat.Ngomong-ngomong, satu-satunya alasan ini berfungsi adalah karena atribut instance menimpa deskriptor non-data . Deskriptor data memiliki
__set__
metode, sedangkan metode (untungnya bagi Anda) tidak. Deskriptor data di kelas sebenarnya mengambil prioritas di atas atribut instance apa pun. Itulah mengapa Anda dapat menetapkan ke properti:__set__
metode mereka dipanggil saat Anda mencoba membuat tugas. Saya pribadi ingin mengambil langkah lebih jauh dan menyembunyikan nilai sebenarnya dari atribut yang mendasari di instance__dict__
, di mana itu tidak dapat diakses dengan cara normal persis karena properti membayangi itu.Anda juga harus ingat bahwa ini tidak ada gunanya untuk metode sulap (garis bawah ganda) . Metode sihir tentu saja dapat diganti dengan cara ini, tetapi operasi yang menggunakannya hanya melihat jenisnya. Misalnya, Anda dapat mengatur
__contains__
ke sesuatu yang istimewa dalam contoh Anda, tetapi panggilanx in instance
akan mengabaikannya dantype(instance).__contains__(instance, x)
sebagai gantinya. Ini berlaku untuk semua metode ajaib yang ditentukan dalam model data Python .sumber
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!!
Anda dapat menggunakan
boby
variabel di dalam fungsi jika Anda membutuhkannya. Karena Anda mengganti metode hanya untuk satu objek contoh ini, cara ini lebih sederhana dan memiliki efek yang sama persis seperti menggunakanself
.sumber
patching
dan ini adalah cara yang benar untuk melakukannya (misalnyaboby = Dog()
danboby.bark = new_bark
). Ini sangat berguna dalam pengujian unit untuk kontrol. Untuk penjelasan selengkapnya, lihat tryolabs.com/blog/2013/07/05/run-time-method-patching-python (contoh) - tidak, saya tidak berafiliasi dengan situs atau penulis yang ditautkan.Karena tidak ada yang menyebutkan di
functools.partial
sini: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!!
sumber
Karena fungsi adalah objek kelas pertama dalam Python, Anda dapat meneruskannya saat menginisialisasi objek kelas Anda atau menimpanya kapan saja untuk instance kelas tertentu:
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()
dan hasilnya adalah
sumber
Meskipun saya menyukai ide pewarisan dari S.Lott dan setuju dengan hal 'type (a)', tetapi karena fungsi juga memiliki atribut yang dapat diakses, saya pikir itu dapat dikelola dengan cara ini:
class Dog: def __init__(self, barkmethod=None): self.bark=self.barkp if barkmethod: self.bark=barkmethod def barkp(self): """original bark""" print "woof" d=Dog() print "calling original bark" d.bark() print "that was %s\n" % d.bark.__doc__ def barknew(): """a new type of bark""" print "wooOOOoof" d1=Dog(barknew) print "calling the new bark" d1.bark() print "that was %s\n" % d1.bark.__doc__ def barknew1(): """another type of new bark""" print "nowoof" d1.bark=barknew1 print "another new" d1.bark() print "that was %s\n" % d1.bark.__doc__
dan hasilnya adalah:
calling original bark woof that was original bark calling the new bark wooOOOoof that was a new type of bark another new nowoof that was another type of new bark
sumber
Sayang ini tidak menimpa Anda hanya memanggil fungsi yang sama dua kali dengan objek. Pada dasarnya menimpa terkait dengan lebih dari satu kelas. ketika metode tanda tangan yang sama ada di kelas yang berbeda maka fungsi mana yang Anda panggil ini memutuskan objek yang memanggil ini. Penimpaan dimungkinkan dalam python ketika Anda membuat lebih dari satu kelas menulis fungsi yang sama dan satu hal lagi untuk dibagikan bahwa overloading tidak diperbolehkan di python
sumber
Saya menemukan ini sebagai jawaban paling akurat untuk pertanyaan asli
https://stackoverflow.com/a/10829381/7640677
import a def _new_print_message(message): print "NEW:", message a.print_message = _new_print_message import b b.execute()
sumber