Python 2.x memiliki dua cara untuk membebani operator perbandingan, __cmp__
atau "operator perbandingan kaya" seperti __lt__
. Perbandingan yang kaya kelebihan dikatakan lebih disukai, tetapi mengapa demikian?
Operator perbandingan kaya lebih sederhana untuk menerapkan masing-masing, tetapi Anda harus mengimplementasikan beberapa di antaranya dengan logika yang hampir identik. Namun, jika Anda dapat menggunakan cmp
pengurutan builtin dan tuple, maka __cmp__
cukup sederhana dan memenuhi semua perbandingan:
class A(object):
def __init__(self, name, age, other):
self.name = name
self.age = age
self.other = other
def __cmp__(self, other):
assert isinstance(other, A) # assumption for this example
return cmp((self.name, self.age, self.other),
(other.name, other.age, other.other))
Kesederhanaan ini tampaknya memenuhi kebutuhan saya jauh lebih baik daripada membebani semua 6 (!) Dari perbandingan yang kaya. (Namun, Anda dapat menurunkannya menjadi "hanya" 4 jika Anda mengandalkan "argumen yang ditukar" / perilaku yang tercermin, tetapi itu menghasilkan peningkatan bersih komplikasi, menurut pendapat saya.)
Apakah ada jebakan tak terduga yang perlu saya waspadai jika saya hanya kelebihan beban __cmp__
?
Saya memahami <
, <=
,==
, dll operator dapat kelebihan beban untuk tujuan lain, dan dapat kembali setiap objek yang mereka suka. Saya tidak bertanya tentang manfaat pendekatan itu, tetapi hanya tentang perbedaan ketika menggunakan operator ini untuk perbandingan dalam arti yang sama yang mereka maksudkan untuk angka.
Pembaruan: Seperti yang ditunjukkan Christopher , cmp
menghilang di 3.x. Apakah ada alternatif lain yang membuat perbandingan penerapan semudah di atas __cmp__
?
sumber
Jawaban:
Ya, mudah untuk menerapkan semuanya dalam hal misalnya
__lt__
dengan kelas mixin (atau metaclass, atau dekorator kelas jika selera Anda seperti itu).Sebagai contoh:
Sekarang kelas Anda dapat mendefinisikan just
__lt__
dan multiply inherit dari ComparableMixin (setelah basis lain apa pun yang dibutuhkan, jika ada). Dekorator kelas akan sangat mirip, hanya dengan memasukkan fungsi yang serupa sebagai atribut kelas baru yang didekorasi (hasilnya mungkin lebih cepat secara mikroskopis saat runtime, dengan biaya yang sama menitnya dalam hal memori).Tentu saja, jika kelas Anda memiliki cara yang sangat cepat untuk diimplementasikan (misalnya)
__eq__
dan__ne__
, itu harus mendefinisikannya secara langsung sehingga versi mixin tidak digunakan (misalnya, untuk kasusdict
) - sebenarnya__ne__
mungkin juga didefinisikan untuk memfasilitasi itu sebagai:tetapi pada kode di atas saya ingin menjaga simetri yang menyenangkan dengan hanya menggunakan
<
;-). Mengenai mengapa__cmp__
harus pergi, karena kita memang punya__lt__
dan teman, mengapa menyimpan cara lain yang berbeda untuk melakukan hal yang persis sama? Itu terlalu banyak bobot mati di setiap runtime Python (Klasik, Jython, IronPython, PyPy, ...). Kode yang pasti tidak memiliki bug adalah kode yang tidak ada - dari mana prinsip Python bahwa seharusnya ada satu cara yang ideal untuk melakukan tugas (C memiliki prinsip yang sama di bagian "Spirit of C" dari standar ISO, btw).Ini tidak berarti kita pergi keluar dari cara kami untuk melarang hal-hal (misalnya, dekat-kesetaraan antara mixin dan dekorator kelas untuk beberapa penggunaan), tapi pasti tidak berarti bahwa kita tidak suka untuk membawa sekitar kode dalam compiler dan / atau runtime yang ada secara mubazir hanya untuk mendukung beberapa pendekatan yang setara untuk melakukan tugas yang persis sama.
Pengeditan lebih lanjut: sebenarnya ada cara yang lebih baik untuk memberikan perbandingan DAN hashing untuk banyak kelas, termasuk dalam pertanyaan -
__key__
metode, seperti yang saya sebutkan di komentar saya untuk pertanyaan tersebut. Karena saya tidak pernah sempat menulis PEP untuk itu, Anda saat ini harus mengimplementasikannya dengan Mixin (& c) jika Anda suka:Ini adalah kasus yang sangat umum untuk perbandingan sebuah instance dengan instance lain yang bermuara pada perbandingan tupel untuk masing-masing dengan beberapa bidang - dan kemudian, hashing harus diterapkan pada dasar yang sama persis. The
__key__
alamat metode khusus yang perlu langsung.sumber
TypeError: Cannot create a consistent method resolution order (MRO) for bases object, ComparableMixin
ketika saya mencoba ini dengan Python 3. Lihat kode lengkapnya di gist.github.com/2696496functools.total_ordering
alih-alih membangunnya sendiriComparableMixim
. Seperti yang disarankan dalam jawaban jmagnusson<
untuk mengimplementasikan__eq__
dalam Python 3 adalah ide yang sangat buruk, karenaTypeError: unorderable types
.Untuk menyederhanakan kasus ini, ada dekorator kelas dengan Python 2.7 + / 3.2 +, functools.total_ordering , yang dapat digunakan untuk mengimplementasikan apa yang disarankan Alex. Contoh dari dokumen:
sumber
total_ordering
tidak diimplementasikan__ne__
, jadi hati-hati!__ne__
. tapi itu karena__ne__
memiliki implementasi default yang didelegasikan__eq__
. Jadi tidak ada yang perlu diperhatikan di sini.Ini tercakup dalam PEP 207 - Rich Comparisons
Juga,
__cmp__
hilang di python 3.0. (Perhatikan bahwa ini tidak ada di http://docs.python.org/3.0/reference/datamodel.html tetapi IS di http://docs.python.org/2.7/reference/datamodel.html )sumber
(Diedit 17/6/17 untuk mempertimbangkan komentar.)
Saya mencoba jawaban mixin yang sebanding di atas. Saya mengalami masalah dengan "Tidak ada". Berikut adalah versi modifikasi yang menangani perbandingan kesetaraan dengan "Tidak Ada". (Saya tidak melihat alasan untuk mempermasalahkan perbandingan ketidaksetaraan dengan Tidak Ada yang kurang semantik):
sumber
self
bisa menjadi tunggalNone
dariNoneType
dan pada saat yang sama menerapkan AndaComparableMixin
? Dan memang resep ini buruk untuk Python 3.self
akan tidak pernah menjadiNone
, sehingga cabang dapat pergi sepenuhnya. Jangan gunakantype(other) == type(None)
; cukup gunakanother is None
. Daripada khusus casingNone
, tes jika jenis lainnya adalah turunan dari jenisself
, dan mengembalikanNotImplemented
tunggal jika tidak:if not isinstance(other, type(self)): return NotImplemented
. Lakukan ini untuk semua metode. Python kemudian akan memberi operan lain kesempatan untuk memberikan jawaban.Terinspirasi oleh Alex Martelli
ComparableMixin
&KeyedMixin
Answers, saya datang dengan mixin berikut. Ini memungkinkan Anda untuk menerapkan_compare_to()
metode tunggal , yang menggunakan perbandingan berbasis kunci yang mirip denganKeyedMixin
, tetapi memungkinkan kelas Anda untuk memilih kunci perbandingan yang paling efisien berdasarkan jenisnyaother
. (Perhatikan bahwa mixin ini tidak banyak membantu untuk objek yang dapat diuji kesetaraannya tetapi tidak untuk urutannya).sumber