最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

python - How to "collect" all items associated with a chain of superclasses? - Stack Overflow

programmeradmin2浏览0评论

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.
Add a comment  | 

1 Answer 1

Reset to default 4

You 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.

发布评论

评论列表(0)

  1. 暂无评论