I understand mixin as what looks like inheritance but what is more like composition.
(edit: I tend to think giving additional functionality/attributes
by mixin rather than giving another is-a relationship
.)
Mentally, I'm saying something like this when I use mixin: I'm giving you this mixin you are missing, rather than you are actually this mixin-type as well.(is-a)
And I read few times, you should prefer composition over inheritance.
We could just use straight compositions instead of mixins, what is mixin for?
If I have to guess, it's because my_instance.foo() is easier than my_instance.another_instance.foo()?
(You can use my_instance.foo() if mixin has foo(), you need my_instance.another_instance.foo() when you composite another_instance as an attribute of my_instance)
Are there any other reason?
Edit:
So even though I feel it's has-a, mixin is still is-a relationship. and benefit you get when you use is-a here is, cleaner interface. That' how I interpret delnan's answer.
(Since mixin doesn't give you is-a relationship, it gives you has-a)
Wrong, it does give you a is-a relationship. Consider class C(A, B)
. issubclass(C, A)
is true and so is issubclass(C, B)
. The call C().method_of_B()
calls the method of B if it isn't overridden somewhere in the MRO (which can happen with single inheritance too!), and similarly for methods of A
. The attribute access C().attr_of_{a,b}
gives you an attribute of A
or B
too (same caveat as with methods).
And I read few times, you should prefer composition over inheritance.
This rule of thumb exists because many people tend to use inheritance where it isn't appropriate, not because inheritance isn't a useful tool in any case. This applies to Python too. Common (good) reasons include:
super()
calls right.If I have to guess, it's because my_instance.foo() is easier than my_instance.another_instance.foo()?
That's just a few additional characters. More important is that you have to re-type wrapper methods for every single method you want to re-export (which would be all of them if you're emulating an is-a relationship via composition).
More conceptually, this manual work seems pointless and error-prone if C
really is-a A
. Mixins/multiple inheritance support the case where an object is more than what can or should be represented by a single bass class. For example, a UTF-8 string may be both a ByteBuffer
and a UnicodeStream
. In general, we have interfaces for this (or not; in Python this is implicit), but mixins also allow adding related functionality in a composable and convenient manner.