Problem
Let's say I have a hierarchy of classes where each class has an associated list of class-specific items, e.g.
class Thing:
Items = ['Exist']
class Being(Thing):
Items = ['Live', 'Die']
class Human(Being):
Items = ['Read' 'Write']
I'd like to define a function GetAllItems() that for any class in the hierarchy, will return its own items and those of its superclasses, i.e:
Thing.GetAllItems() # ==> ['Exist']
Being.GetAllItems() # ==> ['Live', 'Die', 'Exist']
Human.GetAllItems() # ==> ['Read', 'Write', 'Live', 'Die', 'Exist']
It doesn't have to be static methods, but after all, the items that I want to be able to "accumulate" are fundamentally Class-level properties.
What I tried
I was thinking that I could write some kind of simple recursive function, along the lines of:
class A:
Items = [A1, A2, A3]
def GetAllItems(): # not sure how to do this for a static method
return getattr(type(self),'Items') + super().GetAllItems()
and then have this function work in all the derived classes, i.e. B(A), C(B), etc, but that doesn't really work.
The closest I got was to specify a different Items variable in each class, and then provide the (non-static) GetAllItems() function for each class like so:
class A:
ItemsA = [A1, A2, A3]
def GetAllItems():
return A.ItemsA # this is the "root" function
class B(A):
ItemsB = [B1, B2, B3]
def GetAllItems():
return B.ItemsB + super().GetAllItems()
class C(B):
ItemsC = [C1, C2, C3]
def GetAllItems():
return C.ItemsC + super().GetAllItems()
While it's not the end of the world to add a function like this to each derived class, I do have a lot of classes, and it would be nice to be able to do this with a single piece of code somehow without copying and pasting basically the same thing everywhere... Plus I still don't fully understand how super() works, so I feel like there's maybe a more elegant solution to this.
*** UPDATE ***
Just wanted to share a version that seems to work in my simple case (no multiple inheritance)
class Thing:
Items = ['Exist']
@classmethod
def GetAllItems(cls):
return cls.Items
class Being(Thing):
Items = ['Live', 'Die']
@classmethod
def GetAllItems(cls):
return cls.Items + cls.__base__.GetAllItems()
and then all classes derived from Being don't need any additional GetAllItems() declarations. I think this could work for a simple inheritance chain, i.e. B(A), C(B), D(C), etc -- but for more general cases, like multiple inheritance, I think @D. Foley has the right recommendation based on cls.mro()
Problem
Let's say I have a hierarchy of classes where each class has an associated list of class-specific items, e.g.
class Thing:
Items = ['Exist']
class Being(Thing):
Items = ['Live', 'Die']
class Human(Being):
Items = ['Read' 'Write']
I'd like to define a function GetAllItems() that for any class in the hierarchy, will return its own items and those of its superclasses, i.e:
Thing.GetAllItems() # ==> ['Exist']
Being.GetAllItems() # ==> ['Live', 'Die', 'Exist']
Human.GetAllItems() # ==> ['Read', 'Write', 'Live', 'Die', 'Exist']
It doesn't have to be static methods, but after all, the items that I want to be able to "accumulate" are fundamentally Class-level properties.
What I tried
I was thinking that I could write some kind of simple recursive function, along the lines of:
class A:
Items = [A1, A2, A3]
def GetAllItems(): # not sure how to do this for a static method
return getattr(type(self),'Items') + super().GetAllItems()
and then have this function work in all the derived classes, i.e. B(A), C(B), etc, but that doesn't really work.
The closest I got was to specify a different Items variable in each class, and then provide the (non-static) GetAllItems() function for each class like so:
class A:
ItemsA = [A1, A2, A3]
def GetAllItems():
return A.ItemsA # this is the "root" function
class B(A):
ItemsB = [B1, B2, B3]
def GetAllItems():
return B.ItemsB + super().GetAllItems()
class C(B):
ItemsC = [C1, C2, C3]
def GetAllItems():
return C.ItemsC + super().GetAllItems()
While it's not the end of the world to add a function like this to each derived class, I do have a lot of classes, and it would be nice to be able to do this with a single piece of code somehow without copying and pasting basically the same thing everywhere... Plus I still don't fully understand how super() works, so I feel like there's maybe a more elegant solution to this.
*** UPDATE ***
Just wanted to share a version that seems to work in my simple case (no multiple inheritance)
class Thing:
Items = ['Exist']
@classmethod
def GetAllItems(cls):
return cls.Items
class Being(Thing):
Items = ['Live', 'Die']
@classmethod
def GetAllItems(cls):
return cls.Items + cls.__base__.GetAllItems()
and then all classes derived from Being don't need any additional GetAllItems() declarations. I think this could work for a simple inheritance chain, i.e. B(A), C(B), D(C), etc -- but for more general cases, like multiple inheritance, I think @D. Foley has the right recommendation based on cls.mro()
Share Improve this question edited Feb 18 at 5:05 Alex Sotka asked Feb 16 at 6:24 Alex SotkaAlex Sotka 31 silver badge2 bronze badges New contributor Alex Sotka is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.1 Answer
Reset to default 4You can solve this problem elegantly using Python's mro() (Method Resolution Order) to accumulate the Items attributes from all superclasses dynamically. Here's a clean solution:
class Thing:
Items = ['Exist']
@classmethod
def GetAllItems(cls):
all_items = []
for base in reversed(cls.mro()): # Traverse the MRO in reverse order (from base to derived)
if hasattr(base, 'Items'):
all_items.extend(base.Items)
return all_items
class Being(Thing):
Items = ['Live', 'Die']
class Human(Being):
Items = ['Read', 'Write']
# Testing
print(Thing.GetAllItems()) # ['Exist']
print(Being.GetAllItems()) # ['Exist', 'Live', 'Die']
print(Human.GetAllItems()) # ['Exist', 'Live', 'Die', 'Read', 'Write']
Explanation:
Using cls.mro(): This retrieves the method resolution order (MRO) of the class, which lists the class and its base classes in inheritance order. Iterating in Reverse Order: We iterate in reverse so that base class items appear first. Class Method (@classmethod): Allows calling GetAllItems() on the class itself without needing an instance. Checking for Items: The check hasattr(base, 'Items') ensures only classes with an Items attribute contribute to the list.
Why This Works:
No need to manually redefine GetAllItems() in every subclass. Automatically works for any class hierarchy without modifications. Uses a single method in the base class that dynamically resolves items.