On Sat, 16 Apr 2022 at 14:33, Steven D'Aprano <st...@pearwood.info> wrote: > > On Sat, Apr 16, 2022 at 11:07:00AM +1000, Chris Angelico wrote: > > > > > My view: If a class inherits two parents, in any meaning of the word > > > > "inherits" that follows the normal expectation that a subclass IS an > > > > instance of its parent class(es), then it's MI. > > > > > > Inheritance and "is-a" relationships are independent. > > > > > > In some languages (but not Python), mixins provide inheritance but not > > > an "is-a" relationship. In Python, virtual subclassing provides "is-a" > > > without inheritance. > > > > Virtual subclassing is still subclassing, just implemented > > differently. > > You are correct that virtual subclassing is still subclassing, but it > doesn't provide inheritance. > > >>> from abc import ABC > >>> class Parrot(ABC): > ... def speak(self): > ... print("Polly wants a cracker!") > ... > >>> @Parrot.register > ... class Norwegian_Blue: > ... pass > ... > >>> bird = Norwegian_Blue() > >>> isinstance(bird, Parrot) > True > >>> bird.speak() > Traceback (most recent call last): > File "<stdin>", line 1, in <module> > AttributeError: 'Norwegian_Blue' object has no attribute 'speak'
Yes. I would have to call this an abuse of a feature - you're registering that a Norwegian Blue is a Parrot, without it being able to do anything that a parrot should be able to. (Which I presume was your intention, given your choice of examples.) You can claim that it's a parrot all you like, and Python will happily reflect your declaration when you ask if it's a parrot, but it's a pathological case. It's like designing a class with no __str__ method, or one where __add__ takes three parameters, or in any other way violates normal expectations; you can use perfectly normal inheritance to derive from object, but then you make changes so it no longer "behaves-like-a", ie it violates Liskov. > > What is "inheritance" if it isn't that is-a relationship? How do you > > distinguish inheritance from delegation? > > Haven't you spent all of this thread quite happily distinguishing > between using inheritance and composition/delegation until now? Okay. Yes. I am asking you to now define the difference. My understanding was that inheritance was defined by the "is-a" relationship, and delegation was defined by other things. Now that you're claiming that inheritance is NOT defined by an is-a, I want you to define it. > Subclassing is, as you say, an "is-a" relationship. Whether you use > virtual subclassing or actual subclassing, if you can say: > > issubclass(Norwegian_Blue, Parrot) > isinstance(Norwegian_Blue(), Parrot) > > and get True for both of them, then Norwegian_Blue "is-a" Parrot. > > (Note that there is a technical difference between subclass and subtype, > which I don't think is relevent to Python, but let's not go there.) > > Inheritance is a mechanism where a "child object" automatically acquires > the properties and behaviors of some "parent object". In Python, that is > handled by the interpreter when the child subclasses from the parent > (but not in virtual subclassing). > > The critical thing is that in the absense of overloading or overriding, > calling Norwegian_Blue().speak() will inherit the method defined in > Parrot. We get that inheritance from real, but not virtual, subclassing. Is it still inheritance if there needs to be a boatload of code to make this happen? Because SOM/CORBA and the IDL want to have a word with you. In a good high-level language, there should normally be a convenient way to do this without too much effort. But is that really essential to it being inheritance? As another example: In C++, a function accepting a pointer to a base class can be given a parameter that points to a subclass. In the case of multiple inheritance, this can actually mean that the pointer has to change. So, two questions: (1) Is the fact that a function, not defined outside in the class at all, accepts a pointer, part of the behaviour of the class? And (2) If the compiler has to know "to turn a pointer-to-X into a pointer-to-Y, add an offset of 16", is it still retaining functionality? You make a *lot* of assumptions based on Python, which simply don't hold up in other languages, and then you assert that this is some kind of absolute completeness. > Composition provides a "has-a" relationship. For the sake of brevity, in > simple terms (which may not be completely accurate, please don't nit- > pick just for the sake of nit-picking), we use composition when we > write: > > class Car: > def __init__(self): > self.engine = Engine() > > In this example, Cars are not Engines, but they have an Engine. Hence > composition. > > Delegation provides a mechanism of code-sharing separate from > inheritance, and often used instead of inheritance, or to compliment it. > One object delegates to another object if the first explicitly calls the > second: > > # self is a Car instance. > self.engine.start() > > Delegation and composition often go together, but they don't necessarily > have to. One can delegate to an object "outside" of the class, although > that technique is not often used in Python. > > But broadly speaking, if your instance uses: > > super().method() > > that's an inheritance call. (It is only needed when you overload > method.) If you write something like: > > SomeClass.method(self) > > that's delegation. Hmm. I think that's WAY too restrictive a definition, and based on that, a huge number of C++ classes simply don't use inheritance. Or is this kind of restriction specific to Python in some way? Please, can you define inheritance in a way that *doesn't* assume Python 2.3+? > Objects can delegate to anything they like, but if they delegate to a > superclass, we might call it inheritance even though it lacks the > property of being automatically handled by the interpreter. So one might > loosely say that inheritance is a special case of delegation. Ehh, okay. If that's the case, then sure, but we still need a way to define inheritance that doesn't depend on Python-specific concepts. > Note that in languages without any form of super() or "next_class" or > whatever you call it, that sort of manual delegation may be the only way > to overload an inherited method. > > > > Is inheritance only a thing > > if it happens on the line of code that says "class X"? (Not the case > > in Pike.) > > I don't think the syntax is important. You could say: > > @inherits_from(Parrot) > class Norwegian_Blue: > pass > > if you prefer. Defining the decorator is left as an exercise. > > I can't comment on Pike, since you haven't described how it behaves. It's a directive inside the class block: class Norwegian_Blue { inherit Parrot; // ... } And if you do it more than once, you get multiple inheritance. The rules aren't the same as in Python, but there's a way to call *every* parent method, which is extremely convenient for constructors. It most definitely is an "is-a" relationship (for instance, an object of type Norwegian_Blue can be passed to a function that expects a Parrot), but it's also able to model some other patterns too, so pigeonholing things is hard. > As I said, in some languages mixins define inheritance without > subclassing; in Python virtual subclasses define subclassing without > inheritance. > > > > Is inheritance only a thing if it happens as the class is > > first created? (Is the case with mixins.) > > I don't understand what you mean here. Do you mean that mixins inject > their methods into the class at creation time, and then no longer are > referenced? That's not what happens in Python. Mixins use the same > method resolution mechanism as other superclasses, which means it > happens on demand, not at creation time. > > >>> class Mixin: > ... def method(self): > ... print("Creation time") > ... > >>> class Spam(Mixin): > ... pass > ... > >>> Mixin.method = lambda self: print("Call time") > >>> Spam().method() > Call time > I'm trying to figure out what you mean by inheritance. You said that mixins aren't inheritance. Or maybe I misread you? It was rather confusing. ChrisA _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-le...@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/6DQGGB2C2TF7FWP3XOYV4GFVHX76K6YU/ Code of Conduct: http://python.org/psf/codeofconduct/