Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>@Oddthinking's answer is not wrong, but I think it misses the <em>real</em>, <em>practical</em> reason Python has ABCs in a world of duck-typing.</p> <p>Abstract methods are neat, but in my opinion they don't really fill any use-cases not already covered by duck typing. Abstract base classes' real power lies in <a href="https://docs.python.org/3/library/abc.html#abc.ABCMeta.__subclasshook__" rel="nofollow noreferrer">the way they allow you to customise the behaviour of <code>isinstance</code> and <code>issubclass</code></a>. (<code>__subclasshook__</code> is basically a friendlier API on top of Python's <a href="https://docs.python.org/3.3/reference/datamodel.html#customizing-instance-and-subclass-checks" rel="nofollow noreferrer"><code>__instancecheck__</code> and <code>__subclasscheck__</code></a> hooks.) Adapting built-in constructs to work on custom types is very much part of Python's philosophy.</p> <p>Python's source code is exemplary. <a href="https://hg.python.org/cpython/file/8e0dc4d3b49c/Lib/_collections_abc.py#l143" rel="nofollow noreferrer">Here</a> is how <code>collections.Container</code> is defined in the standard library (at time of writing):</p> <pre><code>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 </code></pre> <p>This definition of <code>__subclasshook__</code> says that any class with a <code>__contains__</code> attribute is considered to be a subclass of Container, even if it doesn't subclass it directly. So I can write this:</p> <pre><code>class ContainAllTheThings(object): def __contains__(self, item): return True &gt;&gt;&gt; issubclass(ContainAllTheThings, collections.Container) True &gt;&gt;&gt; isinstance(ContainAllTheThings(), collections.Container) True </code></pre> <p>In other words, <em>if you implement the right interface, you're a subclass!</em> ABCs provide a formal way to define interfaces in Python, while staying true to the spirit of duck-typing. Besides, this works in a way that honours the <a href="https://en.wikipedia.org/wiki/Open%E2%80%93closed_principle" rel="nofollow noreferrer">Open-Closed Principle</a>.</p> <p>Python's object model looks superficially similar to that of a more "traditional" OO system (by which I mean Java*) - we got yer classes, yer objects, yer methods - but when you scratch the surface you'll find something far richer and more flexible. Likewise, Python's notion of abstract base classes may be recognisable to a Java developer, but in practice they are intended for a very different purpose.</p> <p>I sometimes find myself writing polymorphic functions that can act on a single item or a collection of items, and I find <code>isinstance(x, collections.Iterable)</code> to be much more readable than <code>hasattr(x, '__iter__')</code> or an equivalent <code>try...except</code> block. (If you didn't know Python, which of those three would make the intention of the code clearest?)</p> <p>I find that I rarely need to write my own ABC - I prefer to rely on duck typing - and I typically discover the need for one through refactoring. If I see a polymorphic function making a lot of attribute checks, or lots of functions making the same attribute checks, that smell suggests the existence of an ABC waiting to be extracted.</p> <p><sub>*without getting into the debate over whether Java is a "traditional" OO system...</sub></p> <hr> <p><strong>Addendum</strong>: Even though an abstract base class can override the behaviour of <code>isinstance</code> and <code>issubclass</code>, it still doesn't enter the <a href="https://www.python.org/download/releases/2.3/mro/" rel="nofollow noreferrer">MRO</a> of the virtual subclass. This is a potential pitfall for clients: not every object for which <code>isinstance(x, MyABC) == True</code> has the methods defined on <code>MyABC</code>.</p> <pre><code>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 </code></pre> <p>Unfortunately this one of those "just don't do that" traps (of which Python has relatively few!): avoid defining ABCs with both a <code>__subclasshook__</code> and non-abstract methods. Moreover, you should make your definition of <code>__subclasshook__</code> consistent with the set of abstract methods your ABC defines.</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload