12 ms·
This distinction is the same as the distinction between structural and nominal typing, described at the end of the article. Abstract base classes require every
by Conlectus 6y ago
This distinction is the same as the distinction between structural and nominal typing, described at the end of the article.
Abstract base classes require everything to extend from a base-level object, and also inherit it's default implementations. This is a nominal (name-based) mechanism.
Protocol (structural-based) subtyping instead only declares what methods something has to have, without requiring it tie itself to either to a concrete parent class and its behaviours.
Edit: since you partially address this, you ask why you'd use structural typing rather than an abstract base class. I'll turn it around and ask why you would use an abstract base class, which requires modifying the children accordingly, when you could instead use a structural type. The usual preference comes down to whether you want to be explicit about implementing it or not, and whether you want to pull in default behaviour.
- Xophmeister 6y ago> The usual preference comes down to whether you want to be explicit about implementing it or not, and whether you want to pull in default behaviour. Thanks :) I definitely prefer things to be explicit; regardless of it being a "Zen of Python" mantra. That said, I do see the value of `Protocol` for non-annotated/legacy code, in that it gives you a convenient mechanism for this. I would be worried about it becoming a crutch, if overused, but definitely better than no annotations at all! (I don't understand your point regarding an ABC's default implementation. Isn't the point of ABCs that they're not base classes, but interfaces that define the methods required without any implementation?)
- yellowapple 6y ago> Abstract base classes require everything to extend from a base-level object, and also inherit it's default implementations. This is a nominal (name-based) mechanism. The very definition of an abstract base class precludes the existence of (meaningful) implementations. If the base class has implementations for the methods expected to be overridden (beyond no-ops or throwing some exception around the lack of an implementation), then it is definitionally not abstract. That is: a protocol and an abstract class achieve the same thing: they declare the existence of methods, and defer implementation of those methods to implementations / child classes (respectively). Interfaces are great, but if you have multiple inheritance as your hammer (as you do in Python), then it's perfectly reasonable for abstract class inheritance to be your nail.
- Conlectus 6y ago(meaningful) isn't as much a get-of-out-jail clause here as you suggest, if I understand correctly. Requiring implementations at all, even if they are broken, moves the time when you find out about a missing definition to runtime, rather than being statically determined at typecheck time. If the not-overriden method isn't commonly called, it's possible for the non-meaningful / raise an exception version to make it to production. Not so if the error happens at typecheck time.
- Xophmeister 6y agoThat is true. However, other linters (e.g., pylint) will check a class satisfies its interface. IIRC, the runtime error for non-implemented abstract methods happens at instantiation time, rather than when said missing method call is attempted.
- yellowapple 6y agoRight, but that's a problem more fundamental to Python and applicable far beyond the "abstract" classes hacked onto it. That is: if the methods were missing entirely, you'd have the same problem, since Python (without the help of external tools) doesn't make any attempt to validate these things at (bytecode) compile time.
- dragonwriter 6y ago> The very definition of an abstract base class precludes the existence of (meaningful) implementations. No, it doesn't. It precludes meaningful implementation of the complete interface, of course, but it doesn't preclude meaningful implementation for some methods, which depend on the other methods do which meaningful implementations are not provided.
- yellowapple 6y agoRight, but those methods which are implemented are not in the set of methods intended for descendant classes to override. If they are in that set, at all, then the base class is by definition not abstract. The fundamental issue is that Python doesn't really have a way to enforce this, so there's ultimately no such thing as an abstract class in Python - there are only classes that act like they're abstract (by replacing all implementations of meant-to-be-overridden methods with no-ops or thrown exceptions).
- dragonwriter 6y ago> Abstract base classes require everything to extend from a base-level object, and also inherit it's default implementations The general OOP concept of an “abstract base class” might, the particular Python implementation does not, because, as well as classic inheritance, it supports the concept of “virtual subclasses” that are registered with, but do not actually inherit from (and thus do not use the implementation of), an abc. It shares with structural subtyping that it only defines a mandated interface, but with nominal subtyping that it requires an explicit statement of intent to conform to the interface before an object is considered to conform.