Reputation: 2249
I would like to read class attributes from each class all the way up the class inheritance chain.
Something like the following:
class Base(object):
def smart_word_reader(self):
for word in self.words:
print(word)
class A(Base):
words = ['foo', 'bar']
class B(A):
words = ['baz']
if __name__ == '__main__':
a = A()
a.smart_word_reader() # prints foo, bar as expected
b = B()
b.smart_word_reader() # prints baz - how I can I make it print foo, bar, baz?
Obviously, each words
attribute is overriding the others, as it should. How can I do something similar that will let me read the words
attribute from each class in the inheritance chain?
Is there a better way I can approach this problem?
Bonus points for something that will work with multiple inheritance chains (in a diamond shape with everything inheriting from Base
in the end).
Upvotes: 2
Views: 121
Reputation: 78650
I would make smart_word_reader
a classmethod
and iterate over reversed(cls.__mro__)
.
class Base:
@classmethod
def smart_word_reader(cls):
the_words = (word for X in reversed(cls.__mro__)
for word in vars(X).get('words', []))
print('\n'.join(the_words))
Demo:
>>> a = A()
>>> b = B()
>>> a.smart_word_reader()
foo
bar
>>> b.smart_word_reader()
foo
bar
baz
edit
subtle bug: We don't want getattr
(consider class C(B): pass
) to look for a missing attribute in child classes. Changed the getattr
call to vars(X).get('words', [])
.
Upvotes: 2
Reputation: 95873
I guess you could introspect the mro
manually, something to the effect of:
In [8]: class Base(object):
...: def smart_word_reader(self):
...: for cls in type(self).mro():
...: for word in getattr(cls, 'words', ()):
...: print(word)
...:
...: class A(Base):
...: words = ['foo', 'bar']
...:
...: class B(A):
...: words = ['baz']
...:
In [9]: a = A()
In [10]: a.smart_word_reader()
foo
bar
In [11]: b = B()
In [12]: b.smart_word_reader()
baz
foo
bar
Or perhaps in reversed
order:
In [13]: class Base(object):
...: def smart_word_reader(self):
...: for cls in reversed(type(self).mro()):
...: for word in getattr(cls, 'words', ()):
...: print(word)
...:
...: class A(Base):
...: words = ['foo', 'bar']
...:
...: class B(A):
...: words = ['baz']
...:
In [14]: a = A()
In [15]: a.smart_word_reader()
foo
bar
In [16]: b = B()
In [17]: b.smart_word_reader()
foo
bar
baz
Or a more complicated pattern:
In [21]: class Base(object):
...: def smart_word_reader(self):
...: for cls in reversed(type(self).mro()):
...: for word in getattr(cls, 'words', ()):
...: print(word)
...:
...: class A(Base):
...: words = ['foo', 'bar']
...:
...: class B(Base):
...: words = ['baz']
...:
...: class C(A,B):
...: words = ['fizz','pop']
...:
In [22]: c = C()
In [23]: c.smart_word_reader()
baz
foo
bar
fizz
pop
In [24]: C.mro()
Out[24]: [__main__.C, __main__.A, __main__.B, __main__.Base, object]
Upvotes: 4