Zen of Python menyatakan bahwa seharusnya hanya ada satu cara untuk melakukan sesuatu - namun sering kali saya mengalami masalah dalam memutuskan kapan harus menggunakan suatu fungsi versus kapan harus menggunakan suatu metode.
Mari kita ambil contoh sepele- objek ChessBoard. Katakanlah kita membutuhkan cara untuk mendapatkan semua langkah resmi Raja yang tersedia di papan. Apakah kita menulis ChessBoard.get_king_moves () atau get_king_moves (papan_ catur)?
Berikut beberapa pertanyaan terkait yang saya lihat:
- Mengapa python menggunakan 'metode ajaib'?
- Apakah ada alasan string Python tidak memiliki metode panjang string?
Jawaban yang saya dapatkan sebagian besar tidak meyakinkan:
Mengapa Python menggunakan metode untuk beberapa fungsionalitas (misalnya list.index ()) tetapi fungsi untuk yang lain (misalnya len (list))?
Alasan utamanya adalah sejarah. Fungsi digunakan untuk operasi yang generik untuk sekelompok tipe dan yang dimaksudkan untuk bekerja bahkan untuk objek yang tidak memiliki metode sama sekali (misalnya tupel). Juga mudah untuk memiliki fungsi yang dapat dengan mudah diterapkan ke kumpulan objek yang tidak berbentuk saat Anda menggunakan fitur fungsional Python (map (), apply () et al).
Faktanya, mengimplementasikan len (), max (), min () sebagai fungsi bawaan sebenarnya lebih sedikit kode daripada mengimplementasikannya sebagai metode untuk setiap jenis. Seseorang dapat berdebat tentang kasus individu tetapi itu adalah bagian dari Python, dan sudah terlambat untuk membuat perubahan mendasar seperti itu sekarang. Fungsinya harus tetap ada untuk menghindari kerusakan kode besar-besaran.
Meskipun menarik, penjelasan di atas tidak terlalu menjelaskan tentang strategi apa yang harus diadopsi.
Inilah salah satu alasannya - dengan metode khusus, pengembang akan bebas memilih nama metode yang berbeda, seperti getLength (), length (), getlength () atau apa pun. Python memberlakukan penamaan yang ketat sehingga fungsi umum len () bisa digunakan.
Sedikit lebih menarik. Pandangan saya adalah bahwa fungsinya dalam arti tertentu, versi antarmuka Pythonic.
Terakhir, dari Guido sendiri :
Berbicara tentang Kemampuan / Antarmuka membuat saya berpikir tentang beberapa nama metode khusus "nakal" kami. Dalam Referensi Bahasa, dikatakan, "Sebuah kelas dapat mengimplementasikan operasi tertentu yang dipanggil oleh sintaks khusus (seperti operasi aritmatika atau subskrip dan pemotongan) dengan mendefinisikan metode dengan nama khusus." Tetapi ada semua metode ini dengan nama khusus seperti
__len__
atau__unicode__
yang tampaknya disediakan untuk kepentingan fungsi bawaan, daripada untuk dukungan sintaks. Agaknya dalam Python berbasis antarmuka, metode ini akan berubah menjadi metode yang dinamai secara teratur di ABC, jadi itu__len__
akan menjadiclass container: ... def len(self): raise NotImplemented
Padahal, memikirkannya lagi, saya tidak mengerti mengapa semua operasi sintaksis tidak hanya memanggil metode yang dinamai normal yang sesuai pada ABC tertentu. "
<
", misalnya, mungkin akan memanggil "object.lessthan
" (atau mungkin "comparable.lessthan
"). Jadi manfaat lain adalah kemampuan untuk menyapih Python dari keanehan nama yang rusak ini, yang menurut saya merupakan peningkatan HCI .Hm. Saya tidak yakin saya setuju (bayangkan :-).
Ada dua bagian "dasar pemikiran Python" yang ingin saya jelaskan terlebih dahulu.
Pertama-tama, saya memilih len (x) daripada x.len () karena alasan HCI (
def __len__()
datang jauh kemudian). Sebenarnya ada dua alasan yang saling terkait, keduanya HCI:(a) Untuk beberapa operasi, notasi prefiks hanya terbaca lebih baik daripada postfix - operasi prefiks (dan infiks!) memiliki tradisi panjang dalam matematika yang menyukai notasi di mana visual membantu ahli matematika memikirkan suatu masalah. Bandingkan kemudahan kita menulis ulang rumus like
x*(a+b)
intox*a + x*b
dengan kecanggungan melakukan hal yang sama menggunakan notasi OO mentah.(b) Ketika saya membaca kode yang mengatakan
len(x)
saya tahu bahwa itu menanyakan panjang sesuatu. Ini memberi tahu saya dua hal: hasilnya adalah integer, dan argumennya adalah semacam wadah. Sebaliknya, ketika saya membacax.len()
, saya harus tahu bahwa itux
adalah semacam wadah yang mengimplementasikan antarmuka atau mewarisi dari kelas yang memiliki standarlen()
. Saksi kebingungan kita kadang-kadang memiliki ketika kelas yang tidak melaksanakan pemetaan memilikiget()
ataukeys()
metode, atau sesuatu yang tidak file memilikiwrite()
metode.Mengatakan hal yang sama dengan cara lain, saya melihat 'len' sebagai operasi bawaan . Aku benci kehilangan itu. Saya tidak bisa mengatakan dengan pasti apakah Anda bersungguh-sungguh atau tidak, tetapi 'def len (self): ...' jelas terdengar seperti Anda ingin menurunkannya ke metode biasa. Saya sangat -1 tentang itu.
Bagian kedua dari alasan Python yang saya janjikan untuk menjelaskan adalah alasan mengapa saya memilih metode khusus untuk melihat
__special__
dan bukan hanyaspecial
. Saya mengantisipasi banyak operasi yang kelas mungkin ingin timpa, beberapa standar (misalnya__add__
atau__getitem__
), beberapa tidak begitu standar (misalnya acar__reduce__
untuk waktu yang lama tidak memiliki dukungan dalam kode C sama sekali). Saya tidak ingin operasi khusus ini menggunakan nama metode biasa, karena kelas yang sudah ada sebelumnya, atau kelas yang ditulis oleh pengguna tanpa memori ensiklopedis untuk semua metode khusus, akan bertanggung jawab untuk secara tidak sengaja menentukan operasi yang tidak ingin mereka implementasikan , dengan kemungkinan konsekuensi yang menghancurkan. Ivan Krstić menjelaskan hal ini dengan lebih ringkas dalam pesannya, yang muncul setelah saya menulis semua ini.- --Guido van Rossum (halaman muka: http://www.python.org/~guido/ )
Pemahaman saya tentang hal ini adalah bahwa dalam kasus-kasus tertentu, notasi prefiks lebih masuk akal (yaitu, Duck.quack lebih masuk akal daripada quack (Duck) dari sudut pandang linguistik.) Dan sekali lagi, fungsinya memungkinkan untuk "antarmuka".
Dalam kasus seperti itu, tebakan saya adalah mengimplementasikan get_king_moves hanya berdasarkan poin pertama Guido. Tapi itu masih menyisakan banyak pertanyaan terbuka tentang katakanlah, mengimplementasikan stack dan kelas antrian dengan metode push dan pop yang serupa- haruskah itu fungsi atau metode? (di sini saya akan menebak fungsi, karena saya benar-benar ingin memberi sinyal antarmuka push-pop)
TLDR: Dapatkah seseorang menjelaskan apa strategi untuk memutuskan kapan harus menggunakan fungsi vs metode?
X.frob
atauX.__frob__
dan berdiri sendirifrob
.