Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python Mixin for __str__and Method Resolution Order

Tags:

python

mixins

I find that many classes I write in Python contain a small set of variables I actually would like to see when I call str(), and that rewriting __str__(self) for each is rather cumbersome. Thus, I cooked up the following mixin,

class StrMixin(object):
  '''
  Automatically generate __str__ and __repr__
  '''
  def __str__(self):
    import types
    name = self.__class__.__name__ + ': '
    attrs = [ '{}={}'.format(k,v) for (k,v) in self.__dict__.items() ]
    return name + ', '.join(attrs)

  def __repr__(self):
    return str(self)

However, if I write a class,

class C(object, StrMixin):
    pass

I get the following error on instantiation,

TypeError: Error when calling the metaclass bases
    Cannot create a consistent method resolution
order (MRO) for bases object, StrMixin

Granted, including object here is redundant, but what's really going on here?

like image 403
duckworthd Avatar asked Mar 09 '12 09:03

duckworthd


People also ask

What is the method resolution order in Python?

Method Resolution Order (MRO) is an algorithm for the construction of linearization - the list of all the ancestors of a class, including the class itself, ordered from the closest to the furthest. This is the order in which methods and attributes are looked up.

How does Python mixin work?

In Python, so-called mixins are classes that live in the normal inheritance tree, but they are kept small to avoid creating hierarchies that are too complicated for the programmer to grasp. In particular, mixins shouldn't have common ancestors other than object with the other parent classes.

How MRO is calculated in Python?

To get the MRO of a class, you can use either the __mro__ attribute or the mro() method. The __mro__ attribute returns a tuple, but the mro() method returns a python list. To take a more complex example that also demonstrates depth-first search, we take 6 classes. We can represent this with the following diagram.

What is __ MRO __?

Method Resolution Order(MRO) it denotes the way a programming language resolves a method or attribute. Python supports classes inheriting from other classes. The class being inherited is called the Parent or Superclass, while the class that inherits is called the Child or Subclass.


2 Answers

When you define:

class StrMixin(object):
  ...

The compiler knows that StrMixin comes before object in the class's MRO.

When you do:

class C(object, StrMixin):
    pass

You have told the compiler that object comes before StrMixin in the MRO. But object also has to come after StrMixin so it would have to appear twice in the MRO and that isn't allowed.

If you say:

class C(StrMixin, object):
    pass

then the MRO is simply C, StrMixin, object which satisfies the ordering imposed by both classes. There is no duplication because although object is referenced twice there is no conflict between the definitions.

like image 185
Duncan Avatar answered Oct 25 '22 22:10

Duncan


You answered the question yourself - the second object is redundant. Class C has two bases: object and StrMixin. However, StrMixin's base is also object, so its gets confused as to which object it should resolve first. The MRO is calculates it as (C, STRMixin, object, object), which has duplicate objects. In this particular case it seems obvious what the solution should be, but add a few more classes and the MRO could become much less clear. E.g.

class A(object):
    pass
class B(object, A):
    pass
class C(object, A):
    pass
class D(object, B, C):
    pass
class E(object, A, D):
    pass

What is the MRO for E? Whatever it is, its really complicated, has duplicates and probably a few loops.

MRO is explained quite well here, and your specific case is dealt with about two thirds down the page, the first example under "Bad Method Resolution Orders".

like image 24
aquavitae Avatar answered Oct 26 '22 00:10

aquavitae