Saya tahu bahwa Python tidak mendukung metode overloading, tapi saya mengalami masalah yang sepertinya tidak bisa saya selesaikan dengan cara Pythonic yang bagus.
Saya membuat game di mana karakter perlu menembak berbagai peluru, tetapi bagaimana cara saya menulis fungsi yang berbeda untuk membuat peluru ini? Sebagai contoh misalkan saya memiliki fungsi yang membuat peluru bergerak dari titik A ke B dengan kecepatan yang diberikan. Saya akan menulis fungsi seperti ini:
def add_bullet(sprite, start, headto, speed):
... Code ...
Tapi saya ingin menulis fungsi lain untuk membuat peluru seperti:
def add_bullet(sprite, start, direction, speed):
def add_bullet(sprite, start, headto, spead, acceleration):
def add_bullet(sprite, script): # For bullets that are controlled by a script
def add_bullet(sprite, curve, speed): # for bullets with curved paths
... And so on ...
Demikian seterusnya dengan banyak variasi. Apakah ada cara yang lebih baik untuk melakukannya tanpa menggunakan begitu banyak argumen kata kunci menyebabkan semakin cepat jelek. Mengganti nama masing-masing fungsi sangat buruk juga karena Anda mendapatkan baik add_bullet1
, add_bullet2
atau add_bullet_with_really_long_name
.
Untuk menjawab beberapa jawaban:
Tidak, saya tidak dapat membuat hierarki kelas Bullet karena terlalu lambat. Kode aktual untuk mengelola peluru ada di C dan fungsi saya adalah pembungkus di sekitar C API.
Saya tahu tentang argumen kata kunci tetapi memeriksa segala macam kombinasi parameter semakin mengganggu, tetapi argumen default membantu membagikan seperti
acceleration=0
sumber
default value + if + else
untuk melakukan hal yang sama seperti C ++ do. Ini adalah salah satu dari sedikit hal yang dapat dibaca oleh C ++ daripada Python ...script, curve
itu, apakah mereka memiliki leluhur yang sama, metode apa yang mereka dukung. Dengan bebek-mengetik, terserah Anda untuk desain kelas untuk mencari tahu metode apa yang mereka butuhkan untuk mendukung. AgaknyaScript
mendukung semacam callback berbasis timestep (tapi objek apa yang harus dikembalikan? Posisi di timestep itu? Lintasan di timestep itu?). Agaknyastart, direction, speed
danstart, headto, spead, acceleration
keduanya menggambarkan jenis lintasan, tetapi sekali lagi terserah Anda untuk merancang kelas penerima untuk mengetahui cara membongkar mereka dan memprosesnya.Jawaban:
Apa yang Anda minta disebut pengiriman ganda . Lihat contoh-contoh bahasa Julia yang menunjukkan berbagai jenis pengiriman.
Namun, sebelum melihat itu, pertama-tama kita akan membahas mengapa kelebihan tidak benar-benar seperti yang Anda inginkan dengan python.
Kenapa Tidak Berlebihan?
Pertama, kita perlu memahami konsep overloading dan mengapa itu tidak berlaku untuk python.
Python adalah bahasa yang diketik secara dinamis , sehingga konsep overload tidak bisa diterapkan. Namun, semua tidak hilang, karena kita dapat membuat fungsi alternatif seperti itu pada saat run-time:
Jadi kita harus dapat melakukan multimethod dengan python — atau, seperti yang disebut sebagai: pengiriman ganda .
Pengiriman ganda
Metode multimetode juga disebut pengiriman ganda :
Python tidak mendukung ini di luar kotak 1 , tetapi, seperti yang terjadi, ada paket python yang sangat baik yang disebut multipledispatch yang melakukan hal itu.
Larutan
Inilah cara kami menggunakan paket multipledispatch 2 untuk mengimplementasikan metode Anda:
1. Python 3 saat ini mendukung pengiriman tunggal
2. Berhati-hatilah untuk tidak menggunakan multipledispatch dalam lingkungan multi-utas atau Anda akan mendapatkan perilaku aneh.
sumber
speed
mungkin berubah di tengah fungsi ketika utas lain menetapkan nilai sendirispeed
) !!! Butuh waktu lama bagi saya untuk menyadari bahwa perpustakaanlah yang menjadi biang keladinya.Python mendukung "metode overloading" saat Anda mempresentasikannya. Sebenarnya, apa yang baru saja Anda gambarkan adalah sepele untuk diimplementasikan dengan Python, dalam banyak cara yang berbeda, tetapi saya akan menjelaskan:
Dalam kode di atas,
default
adalah nilai default yang masuk akal untuk argumen tersebut, atauNone
. Anda kemudian dapat memanggil metode hanya dengan argumen yang Anda minati, dan Python akan menggunakan nilai default.Anda juga dapat melakukan sesuatu seperti ini:
Alternatif lain adalah langsung menghubungkan fungsi yang diinginkan langsung ke kelas atau instance:
Namun cara lain adalah dengan menggunakan pola pabrik abstrak:
sumber
if sprite and script and not start and not direction and not speed...
hanya untuk mengetahui itu dalam tindakan tertentu. karena seorang penelepon dapat memanggil fungsi yang menyediakan semua parameter yang tersedia. Sementara overloading menentukan untuk Anda set yang tepat dari parameter yang relevan.Anda dapat menggunakan solusi "roll-your-own" untuk fungsi yang berlebihan. Yang ini disalin dari artikel Guido van Rossum tentang multimethods (karena ada sedikit perbedaan antara mm dan overloading dengan python):
Penggunaannya akan
Batasan yang paling membatasi saat ini adalah:
sumber
Opsi yang memungkinkan adalah menggunakan modul multipledispatch seperti yang dijelaskan di sini: http://matthewrocklin.com/blog/work/2014/02/25/Multiple-Dispatch
Alih-alih melakukan ini:
Kamu bisa melakukan ini:
Dengan penggunaan yang dihasilkan:
sumber
Dalam Python 3.4 ditambahkan PEP-0443. Fungsi generik single-dispatch .
Berikut ini deskripsi API pendek dari PEP.
Untuk mendefinisikan fungsi generik, hiasi dengan dekorator @singledispatch. Perhatikan bahwa pengiriman terjadi pada jenis argumen pertama. Buat fungsi Anda sesuai:
Untuk menambahkan implementasi kelebihan fungsi, gunakan atribut register () dari fungsi generik. Ini adalah dekorator, mengambil parameter tipe dan mendekorasi fungsi yang mengimplementasikan operasi untuk tipe itu:
sumber
Jenis perilaku ini biasanya diselesaikan (dalam bahasa OOP) menggunakan Polimorfisme. Setiap jenis peluru akan bertanggung jawab untuk mengetahui bagaimana perjalanannya. Misalnya:
Lewati sebanyak mungkin argumen ke fungsi c_ yang ada, kemudian lakukan pekerjaan menentukan fungsi c mana yang akan dipanggil berdasarkan nilai-nilai pada fungsi c awal. Jadi, python seharusnya hanya memanggil fungsi satu c. Bahwa satu fungsi c melihat argumen, dan kemudian dapat mendelegasikan ke fungsi c lainnya dengan tepat.
Anda pada dasarnya hanya menggunakan setiap subclass sebagai wadah data yang berbeda, tetapi dengan mendefinisikan semua argumen potensial pada kelas dasar, subclass bebas untuk mengabaikan yang mereka tidak melakukan apa-apa.
Ketika jenis peluru baru datang, Anda dapat dengan mudah mendefinisikan satu properti lagi di pangkalan, mengubah satu fungsi python sehingga melewati properti tambahan, dan satu fungsi c_ yang memeriksa argumen dan mendelegasikan dengan tepat. Sepertinya tidak terlalu buruk.
sumber
pos+v*t
dan kemudian membandingkannya dengan batas layarif x > 800
dan sebagainya. Memanggil fungsi ini beberapa ratus kali per frame ternyata sangat lambat. Itu adalah sesuatu seperti 40 fps pada 100% cpu dengan python murni hingga 60 fps dengan 5% -10% bila dilakukan dalam C.add_bullet
, dan ekstrak semua bidang yang Anda butuhkan. Saya akan mengedit jawaban saya.Dengan melewati args kata kunci .
sumber
Baik menggunakan beberapa argumen kata kunci dalam definisi, atau membuat
Bullet
hierarki yang instansnya diteruskan ke fungsi.sumber
Saya pikir persyaratan dasar Anda adalah memiliki sintaks seperti C / C ++ di python dengan sakit kepala sesedikit mungkin. Meskipun saya menyukai jawaban Alexander Poluektov itu tidak bekerja untuk kelas.
Berikut ini harus bekerja untuk kelas. Ia berfungsi dengan membedakan dengan jumlah argumen bukan kata kunci (tetapi tidak mendukung pembedaan berdasarkan jenis):
Dan itu dapat digunakan secara sederhana seperti ini:
Keluaran:
sumber
The
@overload
dekorator ditambahkan dengan jenis petunjuk (PEP 484). Meskipun ini tidak mengubah perilaku python, itu membuatnya lebih mudah untuk memahami apa yang sedang terjadi, dan bagi mypy untuk mendeteksi kesalahan.Lihat: Ketik petunjuk dan PEP 484
sumber
Saya pikir
Bullet
hierarki kelas dengan polimorfisme terkait adalah cara untuk pergi. Anda dapat secara efektif membebani konstruktor kelas dasar dengan menggunakan metaclass sehingga memanggil kelas dasar menghasilkan penciptaan objek subclass yang sesuai. Di bawah ini adalah beberapa kode contoh untuk menggambarkan esensi dari apa yang saya maksud.Diperbarui
Kode telah dimodifikasi untuk dijalankan di bawah Python 2 dan 3 agar tetap relevan. Ini dilakukan dengan cara yang menghindari penggunaan sintaks metaclass eksplisit Python, yang bervariasi antara dua versi.
Untuk mencapai tujuan itu,
BulletMetaBase
instanceBulletMeta
kelas dibuat dengan secara eksplisit memanggil metaclass saat membuatBullet
baseclass (daripada menggunakan__metaclass__=
atribut kelas atau melaluimetaclass
argumen kata kunci tergantung pada versi Python).Keluaran:
sumber
Bullet
subclass tanpa harus memodifikasi kelas dasar atau fungsi pabrik setiap kali Anda menambahkan subtipe lain. (Tentu saja, jika Anda menggunakan C daripada C ++, saya kira Anda tidak memiliki kelas.) Anda juga bisa membuat metaclass yang lebih cerdas yang mengetahui sendiri subkelas apa yang akan dibuat berdasarkan jenis dan / atau nomor argumen yang diteruskan (seperti yang dilakukan C ++ untuk mendukung overloading).Python 3.8 menambahkan functools.singledispatchmethod
Keluaran
Keluaran:
sumber
Gunakan argumen kata kunci dengan default. Misalnya
Dalam kasus peluru lurus versus peluru melengkung, saya akan menambahkan dua fungsi:
add_bullet_straight
danadd_bullet_curved
.sumber
metode overloading rumit di python. Namun, mungkin ada penggunaan melewati dikt, daftar atau variabel primitif.
Saya telah mencoba sesuatu untuk kasus penggunaan saya, ini bisa membantu di sini untuk memahami orang-orang untuk membebani metode.
Mari kita ambil contoh Anda:
metode overload kelas dengan memanggil metode dari kelas yang berbeda.
lewati argumen dari kelas jarak jauh:
ATAU
Jadi, penanganan sedang dilakukan untuk daftar, Kamus atau variabel primitif dari metode overloading.
coba untuk kode Anda.
sumber
Hanya dekorator yang sederhana
Anda bisa menggunakannya seperti ini
Ubah untuk menyesuaikannya dengan use case Anda.
self/this
argumen. Namun, sebagian besar bahasa hanya melakukannya untukthis
argumen saja. Dekorator di atas memperluas ide ke beberapa parameter.Untuk menghapus, asumsikan bahasa statis, dan tentukan fungsinya
Dengan pengiriman statis (overloading) Anda akan melihat "nomor dipanggil" dua kali, karena
x
telah dinyatakan sebagaiNumber
, dan itu semua overloading peduli. Dengan pengiriman dinamis Anda akan melihat "integer disebut, float disebut", karena mereka adalah tipe aktualx
pada saat fungsi dipanggil.sumber
x
untuk pengiriman dinamis, atau dalam urutan mana kedua metode dipanggil untuk pengiriman statis. Rekomendasikan Anda mengedit pernyataan cetak keprint('number called for Integer')
dll.