@Oddthinking la réponse n'est pas mal, mais je crois qu'il manque le réel, pratique de la raison Python a b a ba dans un monde de canard-typage.
Les méthodes abstraites sont utiles, mais les classes de base abstraites " vrai pouvoir réside dans la façon dont ils vous permettent de personnaliser le comportement de l' isinstance
et issubclass
. (__subclasshook__
est essentiellement une plus respectueuses de l'API sur le dessus de Python __instancecheck__
et __subclasscheck__
crochets.) L'adaptation intégrée dans le comportement de travailler sur le type personnalisé est très bien partie de Python de la philosophie.
Pour ré-utiliser l'exemple de l' collections.Container
- ici est la façon dont il est défini dans le standard de la bibliothèque:
class Container:
__metaclass__ = ABCMeta
@abstractmethod
def __contains__(self, x):
return False
@classmethod
def __subclasshook__(cls, C):
if cls is Container:
if _hasattr(C, "__contains__"):
return True
return NotImplemented
Cette définition de l' __subclasshook__
dit que toute la classe avec un __contains__
attribut est considéré comme une sous-classe de Conteneur, même si elle n'est pas sous-classe directement. Donc, je peux écrire ceci:
class ContainAllTheThings(object):
def __contains__(self, item):
return True
print(issubclass(ContainAllTheThings, collections.Container))
# prints True
print(isinstance(ContainAllTheThings(), collections.Container))
# prints True
En d'autres termes, si vous mettez en œuvre le droit de l'interface, vous êtes une sous-classe! Abc fournir un moyen formel de définir des interfaces en Python, tout en restant fidèle à l'esprit de canard-typage. Par ailleurs, cela fonctionne d'une manière qui honore la Ouvert-Fermé Principe.
Parfois, je me retrouve à écrire fonctions polymorphes qui peuvent agir sur un seul élément ou d'un ensemble d'éléments, et je trouve isinstance(x, collections.Iterable)
à être beaucoup plus lisible qu' hasattr(x, '__iter__')
(ou équivalent try...except
bloc. (Si vous ne savez pas Python, lequel de ces trois rendrait l'intention de le code plus clair?)
Je trouve que j'ai rarement besoin d'écrire ma propre ABC, et j'ai généralement découvrir la nécessité pour l'un par la refactorisation. Si je vois une fonction polymorphe de faire beaucoup de l'attribut de vérification, ou beaucoup de fonctions à faire le même attribut de contrôles, l'odeur suggère l'existence d'un ABC en attente d'être extraites.
Addendum: Même si une classe de base abstraite pouvez remplacer le comportement de l' isinstance
et issubclass
, cela ne fonctionne toujours pas entrer dans la MRO de la sous-classe virtuelle. C'est un écueil potentiel pour les clients: pas tous les objets qui isinstance(x, MyABC) == True
a les méthodes définies sur 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
La solution est simple: ne pas définir Abc avec un __subclasshook__
et non de méthodes abstraites. En outre, vous devriez faire votre définition de l' __subclasshook__
compatible avec l'ensemble des méthodes abstraites votre ABC définit.