@ 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):
class Container(metaclass=ABCMeta):
__slots__ = ()
@abstractmethod
def __contains__(self, x):
return False
@classmethod
def __subclasshook__(cls, C):
if cls is Container:
if any("__contains__" in B.__dict__ for B in C.__mro__):
return True
return NotImplemented
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:
class ContainAllTheThings(object):
def __contains__(self, item):
return True
>>> issubclass(ContainAllTheThings, collections.Container)
True
>>> isinstance(ContainAllTheThings(), collections.Container)
True
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 daripada hasattr(x, '__iter__')
atau try...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
dan issubclass
, itu masih tidak memasukkan MRO dari subkelas virtual. Ini adalah jebakan potensial untuk klien: tidak setiap objek yang isinstance(x, MyABC) == True
memiliki metode didefinisikan MyABC
.
class MyABC(metaclass=abc.ABCMeta):
def abc_method(self):
pass
@classmethod
def __subclasshook__(cls, C):
return True
class C(object):
pass
# typical client code
c = C()
if isinstance(c, MyABC): # will be true
c.abc_method() # raises AttributeError
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.
__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.