@ Oddthinking'in cevabı yanlış değil, ama bence Python'un ördek yazma dünyasında ABC'lere sahip olmasının gerçek , pratik nedenini kaçırıyor .
Soyut yöntemler temizdir, ancak bence zaten ördek yazmanın kapsamadığı herhangi bir kullanım durumunu gerçekten doldurmuyorlar. Soyut temel sınıfların gerçek gücü , isinstance
ve davranışlarını özelleştirmenize izin verdiği şekildeissubclass
yatar . ( __subclasshook__
temel olarak Python __instancecheck__
ve__subclasscheck__
kancaların üstünde daha dostça bir API'dir .) Dahili yapıları özel türler üzerinde çalışacak şekilde uyarlamak Python'un felsefesinin büyük bir parçasıdır.
Python'un kaynak kodu örnek niteliğindedir. İşte böyle collections.Container
(yazma anda) standart kütüphanesinde tanımlanır:
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
Bu tanım __subclasshook__
__contains__
özniteliği olan herhangi bir sınıfın, doğrudan alt sınıfı olmasa bile, bir Container alt sınıfı olarak kabul edildiğini belirtir. Yani şunu yazabilirim:
class ContainAllTheThings(object):
def __contains__(self, item):
return True
>>> issubclass(ContainAllTheThings, collections.Container)
True
>>> isinstance(ContainAllTheThings(), collections.Container)
True
Diğer bir deyişle, doğru arayüzü uygularsanız, bir alt sınıfsınız! ABC'ler, ördek yazım ruhuna sadık kalırken, Python'daki arayüzleri tanımlamak için resmi bir yol sağlar. Ayrıca bu, Açık-Kapalı Prensibi'ni onurlandıracak şekilde çalışır .
Python'un nesne modeli, daha "geleneksel" bir OO sistemine (Java * demek istediğim) yüzeysel olarak benziyor - yer sınıflarımız, yer nesneleriniz, yer yöntemleriniz var - ancak yüzeyi çizdiğinizde çok daha zengin bir şey bulacaksınız ve daha esnek. Benzer şekilde, Python'un soyut temel sınıflar kavramı bir Java geliştiricisi tarafından tanınabilir, ancak pratikte çok farklı bir amaca yöneliktir.
Bazen kendimi tek bir eşyaya veya eşya koleksiyonuna etki edebilecek polimorfik fonksiyonlar yazarken buluyorum ve isinstance(x, collections.Iterable)
ve hasattr(x, '__iter__')
eşdeğer bir try...except
bloktan çok daha okunabilir . (Python'u bilmiyorsanız, bu üçünden hangisi kodun amacını en açık hale getirir?)
Bununla birlikte, nadiren kendi ABC'mi yazmam gerektiğini buluyorum ve tipik olarak yeniden düzenleme yoluyla ihtiyacını keşfediyorum. Çok fazla öznitelik denetimi yapan bir polimorfik işlev veya aynı öznitelik denetimini yapan birçok işlev görürsem, bu koku, çıkarılmayı bekleyen bir ABC'nin varlığını gösterir.
* Java'nın "geleneksel" bir OO sistemi olup olmadığı tartışmasına girmeden ...
Zeyilname : soyut bir taban sınıfı davranışını geçersiz kılabilir olsa isinstance
ve issubclass
hala girmezse MRO sanal alt sınıfının. Bu, müşteriler için potansiyel bir tuzaktır: isinstance(x, MyABC) == True
üzerinde tanımlanan yöntemlere sahip her nesne değil 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
Ne yazık ki bu "sadece bunu yapma" tuzaklarından biri (Python'un nispeten az olduğu!): ABC'leri hem a hem de __subclasshook__
soyut olmayan yöntemlerle tanımlamaktan kaçının . Ayrıca, tanımınızı __subclasshook__
ABC'nizin tanımladığı soyut yöntemlerle tutarlı hale getirmelisiniz .
__contains__
bir sınıf ile miras kalan bir sınıf arasındaki farkcollections.Container
nedir? Örneğinizde, Python'da her zaman paylaşılan bir anlayış vardı__str__
. Uygulama__str__
bazı ABC'den miras almak ve sonra uygulamakla aynı vaatlerde bulunur__str__
. Her iki durumda da sözleşmeyi bozabilirsiniz; statik yazımda olduğu gibi kanıtlanabilir bir anlambilim yoktur.