Di Python, apakah ada cara untuk mengikat metode tidak terikat tanpa memanggilnya?
Saya menulis program wxPython, dan untuk kelas tertentu saya memutuskan akan lebih baik untuk mengelompokkan data dari semua tombol saya bersama-sama sebagai daftar tupel tingkat kelas, seperti:
class MyWidget(wx.Window):
buttons = [("OK", OnOK),
("Cancel", OnCancel)]
# ...
def Setup(self):
for text, handler in MyWidget.buttons:
# This following line is the problem line.
b = wx.Button(parent, label=text).Bind(wx.EVT_BUTTON, handler)
Masalahnya adalah, karena semua nilai dari handler
metode tidak terikat, program saya meledak dalam kobaran api yang spektakuler dan saya menangis.
Saya sedang mencari solusi online untuk apa yang tampaknya merupakan masalah yang relatif mudah dan dapat dipecahkan. Sayangnya saya tidak dapat menemukan apa pun. Saat ini, saya menggunakan functools.partial
untuk mengatasi ini, tetapi apakah ada yang tahu jika ada cara Pythonic yang bersih, sehat, untuk mengikat metode tidak terikat ke sebuah instance dan terus menyebarkannya tanpa memanggilnya?
Jawaban:
Semua fungsi juga merupakan deskriptor , sehingga Anda dapat mengikatnya dengan memanggil
__get__
metode mereka :Berikut panduan bagus dari R. Hettinger untuk deskriptor.
Sebagai contoh mandiri ditarik dari komentar Keith :
sumber
MethodType
satu, karena bekerja sama di py3k, sementaraMethodType
argumen telah diubah sedikit.bind = lambda instance, func, asname: setattr(instance, asname, func.__get__(instance, instance.__class__))
Contoh:class A: pass;
a = A();
bind(a, bind, 'bind')
__get__
akan mengambilnya secara implisit dari parameter objek. Saya bahkan tidak yakin apakah memasok itu melakukan apa-apa, karena tidak ada bedanya tipe apa yang saya berikan sebagai parameter kedua terlepas dari apa parameter pertama adalah turunannya. Jadibind = lambda instance, func, asname=None: setattr(instance, asname or func.__name__, func.__get__(instance))
sebaiknya lakukan triknya juga. (Meskipun saya lebih suka memiliki yangbind
dapat digunakan sebagai dekorator, secara pribadi, tetapi itu masalah yang berbeda.)__dict__
dan akses atribut memberi Anda metode tak terikat atau terikat melalui protokol deskriptor normal. Saya selalu berasumsi bahwa itu adalah semacam keajaiban yang terjadi selamatype.__new__()
Ini bisa dilakukan dengan rapi dengan types.MethodType . Contoh:
sumber
__get__
). Saya tidak tahu versi python mana yang Anda uji ini, tetapi pada python 3.4,MethodType
fungsinya membutuhkan dua argumen. Fungsi dan contoh. Jadi ini harus diubah menjaditypes.MethodType(f, C())
.wgt.flush = types.MethodType(lambda self: None, wgt)
Membuat penutupan dengan self di dalamnya tidak akan mengikat fungsi secara teknis, tetapi merupakan cara alternatif untuk memecahkan masalah mendasar yang sama (atau sangat mirip). Berikut contoh sepele:
sumber
functools.partial(handler, self)
Ini akan mengikat
self
kehandler
:Ini bekerja dengan meneruskan
self
sebagai argumen pertama ke fungsi tersebut.object.function()
hanyalah gula sintaksis untukfunction(object)
.sumber
functools.partial
alih-alih menentukan lambda. Itu tidak menyelesaikan masalah yang sebenarnya. Anda masih berurusan dengan a,function
bukaninstancemethod
.function
yang argumen pertama Anda sebagian-ed daninstancemethod
; mengetik bebek tidak bisa melihat perbedaannya.functools.partial
menjatuhkan beberapa metadata, misalnya__module__
. (Juga saya ingin menyatakan untuk catatan bahwa saya merasa ngeri saat melihat komentar pertama saya pada jawaban ini.) Sebenarnya dalam pertanyaan saya, saya menyebutkan bahwa saya sudah menggunakanfunctools.partial
tetapi saya merasa harus ada cara yang "lebih murni", karena mudah untuk mendapatkan metode tak terikat dan terikat.Terlambat ke pesta, tetapi saya datang ke sini dengan pertanyaan serupa: Saya memiliki metode kelas dan contoh, dan ingin menerapkan contoh ke metode.
Dengan risiko menyederhanakan pertanyaan OP, saya akhirnya melakukan sesuatu yang tidak terlalu misterius yang mungkin berguna bagi orang lain yang tiba di sini (peringatan: Saya bekerja dengan Python 3 - YMMV).
Pertimbangkan kelas sederhana ini:
Inilah yang dapat Anda lakukan dengannya:
sumber
meth
dapat dipakai tanpa harus mengirimkana
argumen (itulah sebabnya saya awalnya menggunakanfunctools.partial
) - tetapi ini lebih disukai jika Anda tidak perlu meneruskan metode ini dan bisa saja memintanya di tempat. Ini juga bekerja dengan cara yang sama di Python 2 seperti di Python 3.