Apa itu tambalan monyet?

549

Saya mencoba memahami, apa itu tambalan monyet atau tambalan monyet?

Apakah itu seperti metode / operator yang kelebihan muatan atau pendelegasian?

Apakah ada kesamaan dengan hal-hal ini?

Sergei Basharov
sumber
Saya pikir definisi dari google berguna dan paling umum:Monkey patching is a technique to add, modify, or suppress the default behavior of a piece of code at runtime without changing its original source code.
Charlie Parker

Jawaban:

522

Tidak, tidak seperti semua itu. Ini hanyalah penggantian dinamis atribut saat runtime.

Misalnya, pertimbangkan kelas yang memiliki metode get_data. Metode ini melakukan pencarian eksternal (pada database atau API web, misalnya), dan berbagai metode lain di kelas menyebutnya. Namun, dalam pengujian unit, Anda tidak ingin bergantung pada sumber data eksternal - jadi Anda secara dinamis mengganti get_datametode dengan sebuah rintisan yang mengembalikan beberapa data tetap.

Karena kelas Python bisa berubah, dan metode hanyalah atribut dari kelas, Anda dapat melakukan ini sebanyak yang Anda suka - dan, pada kenyataannya, Anda bahkan dapat mengganti kelas dan fungsi dalam modul dengan cara yang persis sama.

Tapi, seperti yang ditunjukkan komentator , berhati-hatilah saat melakukan monkeypatching:

  1. Jika ada hal lain selain tes logika panggilan Anda get_datajuga, itu juga akan memanggil pengganti monyet Anda yang ditambal daripada yang asli - yang bisa baik atau buruk. Waspadalah.

  2. Jika ada beberapa variabel atau atribut yang juga menunjuk ke get_datafungsi pada saat Anda menggantinya, alias ini tidak akan mengubah artinya dan akan terus menunjuk ke yang asli get_data. (Kenapa? Python hanya me-rebind nama get_datadi kelas Anda ke beberapa objek fungsi lainnya; binding nama lain tidak terpengaruh sama sekali.)

Daniel Roseman
sumber
1
@ LutzPrechelt hanya untuk memperjelas bagi saya, apa maksud Anda pointing to the original get_data function? Maksud Anda saat Anda menyimpan fungsi di dalam variabel jika seseorang mengubah fungsi itu, variabel akan terus menunjuk ke yang lama?
fabriciorissetto
3
@fabriciorissetto: Anda biasanya tidak mengubah objek fungsi dengan Python. Saat Anda menambal monyet get_data, Anda mengubah nama get_datamenjadi fungsi tiruan. Jika beberapa nama lain di tempat lain dalam program terikat ke-fungsi-sebelumnya-dikenal-sebagai- get_data, tidak ada yang akan berubah untuk nama lain itu.
Lutz Prechelt
1
@ LutzPrechelt Bisakah Anda menjelaskan lebih banyak tentang ini?
Calvin Ku
Saya pikir patch monyet dapat berguna terutama untuk debugging, dan dalam fungsi dekorator atau objek pabrik. Namun, ingat eksplisit lebih baik daripada implisit jadi pastikan bahwa kode Anda tidak peka konteks, baca "Goto dianggap berbahaya", dll ...
aoeu256
Jadi, itu hanya sesuatu yang mirip dengan menggunakan fungsi 'eval', di mana Anda bisa memasukkan kode baru saat runtime?
musim dingin
363

MonkeyPatch adalah bagian dari kode Python yang memperluas atau memodifikasi kode lain saat runtime (biasanya saat startup).

Contoh sederhana terlihat seperti ini:

from SomeOtherProduct.SomeModule import SomeClass

def speak(self):
    return "ook ook eee eee eee!"

SomeClass.speak = speak

Sumber: halaman MonkeyPatch di Zope wiki.

Paolo
sumber
126

Apa itu tambalan monyet?

Sederhananya, patch monyet membuat perubahan pada modul atau kelas saat program sedang berjalan.

Contoh dalam penggunaan

Ada contoh tambalan monyet dalam dokumentasi Pandas:

import pandas as pd
def just_foo_cols(self):
    """Get a list of column names containing the string 'foo'

    """
    return [x for x in self.columns if 'foo' in x]

pd.DataFrame.just_foo_cols = just_foo_cols # monkey-patch the DataFrame class
df = pd.DataFrame([list(range(4))], columns=["A","foo","foozball","bar"])
df.just_foo_cols()
del pd.DataFrame.just_foo_cols # you can also remove the new method

Untuk memecah ini, pertama kita mengimpor modul kami:

import pandas as pd

Selanjutnya kita membuat definisi metode, yang ada tidak terikat dan bebas di luar ruang lingkup definisi kelas apa pun (karena perbedaannya cukup berarti antara fungsi dan metode tidak terikat, Python 3 tidak jauh dengan metode tidak terikat):

def just_foo_cols(self):
    """Get a list of column names containing the string 'foo'

    """
    return [x for x in self.columns if 'foo' in x]

Selanjutnya kita cukup melampirkan metode itu ke kelas yang ingin kita gunakan:

pd.DataFrame.just_foo_cols = just_foo_cols # monkey-patch the DataFrame class

Dan kemudian kita bisa menggunakan metode ini pada instance kelas, dan menghapus metode ketika kita selesai:

df = pd.DataFrame([list(range(4))], columns=["A","foo","foozball","bar"])
df.just_foo_cols()
del pd.DataFrame.just_foo_cols # you can also remove the new method

Peringatan untuk nama-mangling

Jika Anda menggunakan susunan-nama (atribut awalan dengan garis bawah-ganda, yang mengubah nama, dan yang tidak saya sarankan), Anda harus memotong-motong nama secara manual jika Anda melakukannya. Karena saya tidak merekomendasikan pembuatan nama, saya tidak akan menunjukkannya di sini.

Contoh Pengujian

Bagaimana kita bisa menggunakan pengetahuan ini, misalnya, dalam pengujian?

Katakanlah kita perlu mensimulasikan panggilan pengambilan data ke sumber data luar yang menghasilkan kesalahan, karena kami ingin memastikan perilaku yang benar dalam kasus seperti itu. Kita dapat menambal monyet struktur data untuk memastikan perilaku ini. (Jadi menggunakan nama metode yang mirip seperti yang disarankan oleh Daniel Roseman :)

import datasource

def get_data(self):
    '''monkey patch datasource.Structure with this to simulate error'''
    raise datasource.DataRetrievalError

datasource.Structure.get_data = get_data

Dan ketika kami mengujinya untuk perilaku yang bergantung pada metode ini meningkatkan kesalahan, jika diterapkan dengan benar, kami akan mendapatkan perilaku itu dalam hasil pengujian.

Hanya dengan melakukan hal di atas akan mengubah Structureobjek selama proses berlangsung, jadi Anda akan ingin menggunakan setup dan teardown di unittests Anda untuk menghindari melakukan itu, misalnya:

def setUp(self):
    # retain a pointer to the actual real method:
    self.real_get_data = datasource.Structure.get_data
    # monkey patch it:
    datasource.Structure.get_data = get_data

def tearDown(self):
    # give the real method back to the Structure object:
    datasource.Structure.get_data = self.real_get_data

(Sementara di atas baik-baik saja, mungkin akan menjadi ide yang baik untuk menggunakan mockperpustakaan untuk menambal kode. mock'S patchdekorator akan kurang rawan kesalahan daripada melakukan hal di atas, yang akan membutuhkan lebih baris kode dan dengan demikian lebih banyak kesempatan untuk memperkenalkan kesalahan Saya belum meninjau kode di mocktapi saya membayangkan menggunakan patch-monyet dengan cara yang sama.)

Aaron Hall
sumber
jadi apakah beban pada monkeypatcher untuk menyimpan referensi ke metode nyata? misal, apa yang terjadi jika seseorang lupa langkah "mempertahankan pointer", itu hilang?
Tommy
3
@Tommy Jika penghitungan ulang ke metode "ditimpa" menjadi nol - ini adalah sampah yang dikumpulkan, dan dengan demikian "hilang" selama umur proses (atau kecuali modul tempat modul itu dimuat ulang, tetapi itu biasanya tidak disarankan).
Aaron Hall
33

Menurut Wikipedia :

Dalam Python, istilah tambalan monyet hanya merujuk pada modifikasi dinamis dari kelas atau modul saat runtime, dimotivasi oleh maksud untuk menambal kode pihak ketiga yang ada sebagai solusi untuk bug atau fitur yang tidak bertindak sesuai keinginan Anda.

David Heffernan
sumber
16

Pertama: patch monyet adalah hack jahat (menurut saya).

Ini sering digunakan untuk mengganti metode pada tingkat modul atau kelas dengan implementasi kustom.

Penggunaan kata kunci yang paling umum adalah menambahkan solusi untuk bug dalam modul atau kelas ketika Anda tidak dapat mengganti kode asli. Dalam hal ini Anda mengganti kode "salah" melalui tambalan monyet dengan implementasi di dalam modul / paket Anda sendiri.

Andreas Jung
sumber
8
Seandainya beberapa modul menambal monyet hal yang sama: Anda akan menemui ajal.
Andreas Jung
49
Walaupun kekuatannya memang bisa berbahaya secara umum, itu bagus untuk pengujian
dkrikun
1
Penggunaan kata kunci yang paling umum sebenarnya untuk pengujian, terutama tes unit. Anda hanya ingin menguji kode Anda, sehingga Anda menambal panggilan eksternal untuk mengembalikan hasil yang diharapkan.
brocoli
1
itu tidak jahat, saya menggunakannya untuk menambal bug dalam perangkat lunak orang lain sampai rilis baru keluar bukannya forking dan menciptakan ketergantungan baru.
nurettin
1
Penambalan monyet dapat dilakukan dengan "cara fungsional murni" bukan cara yang bisa berubah, "peka konteks", seperti halnya dengan hanya melakukan penambalan di dalam dekorator yang mengembalikan versi tambalan baru dari kelas / metode Anda (bukan memodifikasinya). Banyak programmer C # / Java tidak tahu tentang pengembangan yang digerakkan oleh REPL, sehingga mereka membuat kode dalam IDE mereka yang mengharuskan semuanya didefinisikan secara statis. Karena C # / Java tidak memiliki patch-monyet, mereka menganggap kejahatannya ketika mereka melihatnya di JavaScript, Smalltalk, Lisp, Python, dll ... karena bertentangan dengan praktik pengembangan IDE-driven statis mereka.
aoeu256
13

Penambalan monyet hanya bisa dilakukan dalam bahasa yang dinamis, yang contohnya adalah python. Mengubah metode saat runtime alih-alih memperbarui definisi objek adalah salah satu contohnya, sama halnya dengan menambahkan atribut (apakah metode atau variabel) pada saat runtime dianggap sebagai patch monyet. Ini sering dilakukan ketika bekerja dengan modul yang tidak memiliki sumbernya, sehingga definisi objek tidak dapat dengan mudah diubah.

Ini dianggap buruk karena itu berarti bahwa definisi objek tidak sepenuhnya atau akurat menggambarkan bagaimana sebenarnya berperilaku.

Aaron Dufour
sumber
Namun, tambalan monyet dapat bermanfaat selama alih-alih memodifikasi objek atau kelas yang sudah ada, Anda membuat versi baru dari objek dengan tambalan anggota di dalam dekorator yang meneriakkan "hai aku akan menambal Anda".
aoeu256
Anda dapat menggunakan anotasi pada anggota yang ditambal untuk menyimpan anggota yang ditambal yang digunakan untuk menambal di patch. Katakanlah Anda memiliki dekorator yang tidak dapat diurungkan yang membuat versi baru yang tidak dapat diurungkan dari objek fungsi dengan metode undo. Anda dapat menempatkan bidang patcher pada dekorator yang menunjuk ke dekorator Anda yang tidak dapat dikembalikan.
aoeu256
5

Patching monyet membuka kembali kelas yang ada atau metode di kelas saat runtime dan mengubah perilaku, yang harus digunakan dengan hati-hati, atau Anda harus menggunakannya hanya ketika Anda benar-benar perlu.

Karena Python adalah bahasa pemrograman yang dinamis, Kelas bisa berubah sehingga Anda dapat membukanya kembali dan memodifikasi atau bahkan menggantinya.

kamal
sumber
1

Apa itu tambalan monyet? Monkey patching adalah teknik yang digunakan untuk memperbarui perilaku sepotong kode secara dinamis saat dijalankan.

Mengapa menggunakan monkey patching? Itu memungkinkan kita untuk memodifikasi atau memperluas perilaku perpustakaan, modul, kelas atau metode saat runtime tanpa benar-benar memodifikasi kode sumber

Kesimpulan Penambalan monyet adalah teknik keren dan sekarang kami telah belajar bagaimana melakukannya dengan Python. Namun, seperti yang kita diskusikan, ia memiliki kekurangannya sendiri dan harus digunakan dengan hati-hati.

Untuk info lebih lanjut Silakan merujuk [1]: https://medium.com/@nagillavenkatesh1234/monkey-patching-in-python-explained-with-examples-25eed0aea505

akash kumar
sumber