1

I am trying to implicitly concatenate class variables through inheritance. I was able to go up one level but not more... It this actually possible?

class Base(object):
    def get_crumbs(self):
        crumbs = getattr(super(self.__class__, self), 'crumbs', ())
        crumbs += getattr(self, 'crumbs', ())
        return crumbs


class A(Base):
    crumbs = ('un chasseur', )


class B(A):
    crumbs = ('sachant chasser', )


class C(B):
    crumbs = ('sans son chien', 'est un bon chasseur')


>>> c = C()
>>> c.get_crumbs()
>>> <type 'tuple'>: ('sachant chasser', 'sans son chien', 'est un bon chasseur')
martineau
  • 119,623
  • 25
  • 170
  • 301
pawamoy
  • 3,382
  • 1
  • 26
  • 44
  • 4
    Don't ever use `super(self.__class__, self)`; any subclassed method will now enter into infinite recursion. Either use the Python 3 version without arguments, or explicitly name the class. See https://stackoverflow.com/questions/4235078/how-to-avoid-infinite-recursion-with-super – Martijn Pieters May 27 '16 at 16:13
  • Since you're using class variables and tuples anyway, why not `class B(A): crumbs = A.crumbs + ('sachant chasser',)` and `class C(B): crumbs = B.crumbs + ('sans son chien', 'est un bon chasseur')`, and so on? – Two-Bit Alchemist May 27 '16 at 16:14
  • @Two-BitAlchemist I thought of that, yes, but this is why I said **implicitly** :) – pawamoy May 27 '16 at 22:22

1 Answers1

3

You can loop over the class.__mro__ tuple:

class Base(object):
    def get_crumbs(self):
        crumbs = []
        for cls in type(self).__mro__:
            crumbs.extend(getattr(cls, 'crumbs', ()))
        return tuple(crumbs)

Demo:

>>> class Base(object):
...     def get_crumbs(self):
...         crumbs = []
...         for cls in type(self).__mro__:
...             crumbs.extend(getattr(cls, 'crumbs', ()))
...         return tuple(crumbs)
...
>>> class A(Base):
...     crumbs = ('un chasseur',)
...
>>> class B(A):
...     crumbs = ('sachant chasser',)
...
>>> class C(B):
...     crumbs = ('sans son chien', 'est un bon chasseur')
...
>>> c = C()
>>> c.get_crumbs()
('sans son chien', 'est un bon chasseur', 'sachant chasser', 'un chasseur')

Side note: don't use super(self.__class__, self), ever. That'll lead to infinite recursion if you ever override the get_crumbs() method and use super() to call the original, see How to avoid infinite recursion with super()?. Use super(ClassName, self) or (Python 3 only) super(), instead.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • Okay that will do, except that the result is reversed (a simple `for cls in reversed(type(self).__mro__)` did the trick). Thank you! – pawamoy May 27 '16 at 22:24
  • @Pawamoy: ah, sorry, yes, it iterates in search order, so 'closest to the current class' comes first. – Martijn Pieters May 27 '16 at 22:35