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!!
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!!
Jawaban:
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 bobydan 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"
funcTypedan mengapa itu perlu.
funcType = types.MethodType(setelah mengimpor types) daripada funcType = type(Dog.bark).
__get__fungsi untuk mengikatnya ke instance.
Anda perlu menggunakan MethodType dari typesmodul. Tujuannya MethodTypeadalah untuk menimpa metode tingkat instance (sehingga selfdapat 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!!
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 .
kelas Anjing:
def bark (diri):
cetak "Guk"
def new_bark (self):
cetak "Guk Guk"
foo = Anjing ()
# "Guk"
foo.bark ()
# ganti bark dengan new_bark untuk objek ini saja
foo.bark = new_bark .__ get __ (foo, Dog)
foo.bark ()
# "Guk guk"
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 panggilan x in instanceakan mengabaikannya dan type(instance).__contains__(instance, x)sebagai gantinya. Ini berlaku untuk semua metode ajaib yang ditentukan dalam model data Python .
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 bobyvariabel 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 menggunakan self.
patchingdan ini adalah cara yang benar untuk melakukannya (misalnya boby = Dog()dan boby.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.partialsini:
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!!
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
calling original bark
woof
calling the new bark
wooOOOoof
calling another new
nowoof
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
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
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()