Versi pendek
ABC menawarkan tingkat kontrak semantik yang lebih tinggi antara klien dan kelas yang diimplementasikan.
Versi panjang
Ada kontrak antara kelas dan peneleponnya. Kelas berjanji untuk melakukan hal-hal tertentu dan memiliki sifat-sifat tertentu.
Ada beberapa level kontrak.
Pada level yang sangat rendah, kontrak mungkin menyertakan nama metode atau jumlah parameternya.
Dalam bahasa yang diketik secara statis, kontrak itu sebenarnya akan diberlakukan oleh kompiler. Dengan Python, Anda bisa menggunakan EAFP atau mengetik introspeksi untuk mengonfirmasi bahwa objek yang tidak diketahui memenuhi kontrak yang diharapkan ini.
Tetapi ada juga janji semantik tingkat tinggi dalam kontrak.
Sebagai contoh, jika ada __str__()
metode, diharapkan untuk mengembalikan representasi string objek. Itu bisa menghapus semua konten objek, melakukan transaksi dan mengeluarkan halaman kosong dari printer ... tetapi ada pemahaman umum tentang apa yang harus dilakukan, dijelaskan dalam manual Python.
Itu adalah kasus khusus, di mana kontrak semantik dijelaskan dalam manual. Apa yang harus print()
dilakukan metode ini? Haruskah itu menulis objek ke printer atau garis ke layar, atau yang lain? Tergantung - Anda perlu membaca komentar untuk memahami kontrak lengkap di sini. Sepotong kode klien yang hanya memeriksa bahwa print()
metode yang ada telah mengkonfirmasi bagian dari kontrak - bahwa panggilan metode dapat dibuat, tetapi tidak ada kesepakatan tentang semantik tingkat tinggi dari panggilan tersebut.
Mendefinisikan Kelas Basis Abstrak (ABC) adalah cara menghasilkan kontrak antara pelaksana kelas dan penelepon. Ini bukan hanya daftar nama metode, tetapi pemahaman bersama tentang apa yang harus dilakukan metode tersebut. Jika Anda mewarisi dari ABC ini, Anda berjanji untuk mengikuti semua aturan yang dijelaskan dalam komentar, termasuk semantik dari print()
metode ini.
Mengetik bebek Python memiliki banyak keunggulan dalam fleksibilitas dibandingkan pengetikan statis, tetapi tidak menyelesaikan semua masalah. ABC menawarkan solusi perantara antara bentuk bebas Python dan ikatan-dan-disiplin bahasa yang diketik secara statis.
__contains__
dan kelas yang mewarisi daricollections.Container
? Dalam contoh Anda, dalam Python selalu ada pemahaman bersama tentang__str__
. Implementing__str__
membuat janji yang sama dengan mewarisi dari beberapa ABC dan kemudian mengimplementasikan__str__
. Dalam kedua kasus Anda dapat melanggar kontrak; tidak ada semantik yang dapat dibuktikan seperti yang kita miliki dalam pengetikan statis.collections.Container
adalah kasus yang merosot, yang hanya mencakup\_\_contains\_\_
, dan hanya berarti konvensi yang telah ditentukan. Menggunakan ABC tidak menambah banyak nilai dengan sendirinya, saya setuju. Saya menduga itu ditambahkan untuk memungkinkan (misalnya)Set
untuk mewarisinya. Pada saat AndaSet
tiba, tiba-tiba milik ABC memiliki semantik yang cukup. Item tidak dapat menjadi milik koleksi dua kali. Itu TIDAK terdeteksi oleh adanya metode.Set
adalah contoh yang lebih baik daripadaprint()
. Saya sedang berusaha menemukan nama metode yang maknanya ambigu, dan tidak bisa dikuasai oleh namanya saja, jadi Anda tidak bisa yakin itu akan melakukan hal yang benar hanya dengan namanya dan manual Python.Set
sebagai contoh alih-alihprint
?Set
sangat masuk akal, @Oddthinking.@ Jawaban Oddthinking ini tidak salah, tapi saya pikir itu merindukan nyata , praktis alasan Python memiliki ABC di dunia bebek-mengetik.
Metode abstrak rapi, tetapi menurut saya mereka tidak benar-benar mengisi kasus penggunaan yang belum tercakup oleh mengetik bebek. Kekuatan nyata kelas dasar abstrak terletak pada cara mereka memungkinkan Anda untuk menyesuaikan perilaku
isinstance
danissubclass
. (__subclasshook__
pada dasarnya adalah API ramah di atas Python__instancecheck__
dan__subclasscheck__
kait.) Menyesuaikan konstruksi built-in untuk bekerja pada jenis kustom adalah sangat banyak bagian dari filosofi Python.Kode sumber Python adalah contoh. Berikut adalah bagaimana
collections.Container
didefinisikan di perpustakaan standar (pada saat penulisan):Definisi ini
__subclasshook__
mengatakan bahwa setiap kelas dengan__contains__
atribut dianggap sebagai subkelas dari Kontainer, bahkan jika itu bukan subkelas secara langsung. Jadi saya bisa menulis ini:Dengan kata lain, jika Anda mengimplementasikan antarmuka yang tepat, Anda adalah subkelas! ABC memberikan cara formal untuk mendefinisikan antarmuka dengan Python, sambil tetap setia pada semangat mengetik-bebek. Selain itu, ini bekerja dengan cara yang menghormati Prinsip Terbuka-Tertutup .
Model objek Python terlihat sangat mirip dengan sistem OO yang lebih "tradisional" (yang saya maksudkan Java *) - kita mendapatkan kelas, objek, dan metode Anda - tetapi ketika Anda menggores permukaan, Anda akan menemukan sesuatu yang jauh lebih kaya dan lebih fleksibel. Demikian pula, gagasan Python tentang kelas dasar abstrak dapat dikenali oleh pengembang Java, tetapi dalam praktiknya mereka dimaksudkan untuk tujuan yang sangat berbeda.
Kadang-kadang saya menemukan diri saya menulis fungsi polimorfik yang dapat bertindak pada satu item atau kumpulan item, dan saya menemukan
isinstance(x, collections.Iterable)
lebih banyak dibaca daripadahasattr(x, '__iter__')
atautry...except
blok yang setara . (Jika Anda tidak tahu Python, yang mana dari ketiganya akan membuat maksud kode menjadi paling jelas?)Yang mengatakan, saya menemukan bahwa saya jarang perlu menulis ABC saya sendiri dan saya biasanya menemukan kebutuhan untuk itu melalui refactoring. Jika saya melihat fungsi polimorfik melakukan banyak pemeriksaan atribut, atau banyak fungsi membuat pemeriksaan atribut yang sama, bau itu menunjukkan keberadaan ABC yang menunggu untuk diekstraksi.
* tanpa masuk ke perdebatan apakah Jawa adalah sistem OO "tradisional" ...
Tambahan : Meskipun kelas dasar abstrak dapat mengesampingkan perilaku
isinstance
danissubclass
, itu masih tidak memasukkan MRO dari subkelas virtual. Ini adalah jebakan potensial untuk klien: tidak setiap objek yangisinstance(x, MyABC) == True
memiliki metode didefinisikanMyABC
.Sayangnya ini salah satu dari perangkap "hanya jangan lakukan itu" (yang hanya dimiliki sedikit oleh Python!): Hindari mendefinisikan ABC dengan
__subclasshook__
metode a dan non-abstrak. Selain itu, Anda harus membuat definisi Anda__subclasshook__
konsisten dengan serangkaian metode abstrak yang didefinisikan oleh ABC Anda.sumber
isinstance(x, collections.Iterable)
lebih jelas bagi saya, dan saya tahu Python.C
subclass menghapus (atau mengacaukan perbaikan) yangabc_method()
diwarisi dariMyABC
. Perbedaan utama adalah bahwa superclass yang mengacaukan kontrak warisan, bukan subclass.Container.register(ContainAllTheThings)
contoh yang diberikan untuk bekerja?__subclasshook__
adalah "kelas apa pun yang memenuhi predikat ini dianggap sebagai subclass untuk keperluanisinstance
danissubclass
pemeriksaan, terlepas dari apakah itu terdaftar di ABC , dan terlepas dari apakah itu adalah subclass langsung ". Seperti yang saya katakan dalam jawaban, jika Anda mengimplementasikan antarmuka yang tepat, Anda adalah subclass!Fitur praktis dari ABC adalah bahwa jika Anda tidak menerapkan semua metode (dan properti) yang diperlukan, Anda mendapatkan kesalahan saat instantiasi, dan bukannya
AttributeError
, berpotensi jauh di kemudian hari, ketika Anda benar-benar mencoba menggunakan metode yang hilang.Contoh dari https://dbader.org/blog/abstract-base-classes-in-python
Edit: untuk memasukkan sintaks python3, terima kasih @PandasRocks
sumber
Ini akan membuat menentukan apakah suatu objek mendukung protokol yang diberikan tanpa harus memeriksa keberadaan semua metode dalam protokol atau tanpa memicu pengecualian jauh di dalam wilayah "musuh" karena non-dukungan jauh lebih mudah.
sumber
Metode abstrak pastikan bahwa apa pun metode yang Anda panggil di kelas induk harus muncul di kelas anak. Di bawah ini adalah cara biasa memanggil dan menggunakan abstrak. Program ini ditulis dalam python3
Cara panggilan biasa
Dengan metode abstrak
Karena methodtwo tidak dipanggil di kelas anak kami mendapat kesalahan. Implementasi yang tepat di bawah ini
sumber