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'


> 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.

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.

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.

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.

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.

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


-- 
Steve
_______________________________________________
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/QVUS76F7UZTH6WTRZ4F5VATS4IKSRPGS/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to