Nathaniel Ford
Nathaniel Ford

Reputation: 21220

Merging Class Members

Given a class structure like this:

class A:
    dependencies = ["x", "y"]

class B(A):
    dependencies = ["z"]

class C(A):
    dependencies = ["n", "m"]

class D(C):
    dependencies = ["o"]

I want to know if it's possible to write a function (preferably living on class A) that does something along these lines:

@classmethod
def get_all_dependencies(cls):
    return super().get_all_dependencies() + cls.dependencies

For the above classes, the expected output would be:

>>> A.get_all_dependencies():
["x", "y"]
>>> B.get_all_dependencies():
["x", "y", "z"]
>>> C.get_all_dependencies():
["x", "y", "n", "m"]
>>> D.get_all_dependencies():
["x", "y", "n", "m", "o"]

Obviously the above code doesn't work - it just returns the dependencies of the class I call it on. I'm not sure how to get it to work recursively across all the classes? (I'm eliding a hasattr check to ensure the parent class call get_all_dependencies().)

Upvotes: 1

Views: 105

Answers (3)

krieghan
krieghan

Reputation: 177

You can certainly do this if you define "get_all_dependencies" on each class and, instead of cls.dependencies, you reference the class that you're on (ie. super(B, self).get_all_dependences() + B.dependencies)

Upvotes: 0

user2357112
user2357112

Reputation: 280554

Walk the MRO manually:

@classmethod
def get_all_dependencies(cls):
    deps = []
    for c in cls.__mro__:
        # Manual dict lookup to not pick up inherited attributes
        deps += c.__dict__.get('dependencies', [])
    return deps

If you want to deduplicate dependencies that appear repeatedly in the MRO, you can:

from collections import OrderedDict

@classmethod
def get_all_dependencies(cls):
    return list(OrderedDict.fromkeys(dep for c in cls.__mro__
                                         for dep in c.__dict__.get('dependencies', [])))

Upvotes: 4

Dimitris Fasarakis Hilliard
Dimitris Fasarakis Hilliard

Reputation: 160407

Walk the mro and grab the dependencies is what I'd do:

@classmethod
def get_dep(cls):
    return [d for c in cls.mro()[:-1] for d in getattr(c, 'dependencies')]

with cls.mro()[:-1] being used to exclude object.

This returns:

>>> A.get_dep()
['x', 'y']
>>> B.get_dep()
['z', 'x', 'y']
>>> C.get_dep()
['n', 'm', 'x', 'y']
>>> D.get_dep()
['o', 'n', 'm', 'x', 'y']

Upvotes: 6

Related Questions