Łukasz Langa added the comment: The reason why I think it's wrong to special-case ABCs that are explicitly in the MRO is that it's only one of four methods of virtual subclassing:
1. Explicit MRO; 2. Abstract functionality implicitly implemented without any form of registration; 3. abc.register(); 4. One of the above on a base of the type in question. This creates more possibilities for conflicts than just the example described in my last message. For instance, what's the preferred ABC here, Iterable or Sized? >>> class E(Sized): ... def __len__(self): ... return 0 ... def __iter__(self): ... for i in []: ... yield i My answer is: neither. E equally is-a Sized and is-a Iterable. If the dispatcher favors one over the other, you will get people upset about the decision, no matter which one it is. Note that the conflict arises only for multiple ABCs which end up *on the same level* of the MRO. For instance in the example below the dispatch always chooses the Iterable implementation (the functionality appears directly on F, whereas Sized appears on a base): >>> class HasSize(Sized): ... def __len__(self): ... return 0 ... >>> class F(HasSize): ... def __iter__(self): ... for i in []: ... yield i If we wanted to favor the ABCs that are explicitly in the MRO, what should we choose in this case? If we say "Sized", then it breaks the whole idea of arranging the ABCs next to the class that first implements them in the MRO. But it gets better! Suppose you have a generic function with implementations for Sized and Callable. Which implementation should we choose for class G? >>> class G(MutableMapping): ... def __call__(self): ... return None ... # the rest of the MutableMapping implementation Seems like Sized because it is explicitly in the MRO, right? What about H then? >>> class H(dict): ... def __call__(self): ... return None Well, now it's suddenly Callable because Sized is "only" a virtual base class. I don't think we should let that happen. It all comes down to the question whether you consider ABCs to be bases FOR REAL or only sort-of-but-not-really. I believe they're real bases regardless of the method of registration. Implicit implementation and abc.register() doesn't make the base any less real. All in all, the user will ask: "Hey, it's true, I have a tricky type that subclasses both an ABC1 and an ABC2 and singledispatch raises a RuntimeError. How do I make this work?" The answer is simple: just register a more specific implementation on the generic function, even if it simply selects one of the existing ones: generic_func.register(TrickyType, generic_func.dispatch(ABC2)) Explicit is better than implicit. ---------- Added file: http://bugs.python.org/file30711/issue18244.diff _______________________________________ Python tracker <rep...@bugs.python.org> <http://bugs.python.org/issue18244> _______________________________________ _______________________________________________ Python-bugs-list mailing list Unsubscribe: http://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com