Search code examples
pythonpython-3.xmultiple-inheritance

Why does linearization in Python give this bizarre result?


Consider this simplified situation:

class Decoder:
    def __str__(self):
        return self.__bytes__().decode('ascii')

class Comment(Decoder, bytes):
    def __bytes__(self):
        return b'#' + self

Usage:

Comment(b'foo')

Prints:

b'foo'

Instead of expected:

#foo

Regardless of the order in Comment.mro() (i.e. I can swap Decoder and bytes in the supeclass list), Decoder.__str__() is never called.

What gives?


Solution

  • Comment(b'foo') calls Comment.__new__, which, not being defined, resolves to either Decoder.__new__ or bytes.__new__, depending on the order in which you list them in the definition of Comment.

    The MRO for Comment is Comment, bytes, Decoder, object. However, the functions actually being called are:

    1. Comment.__new__, to create a new object. Since that function isn't defined, we next call bytes.__new__, which is defined. It effectively just calls object.__new__(Comment, b'foo'), giving you your final object.

    2. To display the return value of Comment, the interpreter tries to call Comment.__repr__, not Comment.__str__. Again, the function isn't defined, so it falls back to bytes.__repr__, giving the observed result.