LuizFelippe
LuizFelippe

Reputation: 307

How to combine properties defined by different parent classes in python?

Suppose we have two classes and each computes a property stuff in a different way. Is is possible to combine their outputs in a method/property of a child class?

The following code shows the desired effect (though get_stuff_from needs to be replaced by a proper python construct, if such thing exists).

class Foo():
    @property
    def stuff(self):
        return ['a','b','c']

class Bar():
    @property
    def stuff(self):
        return ['1','2','3']

class FooBar(Foo, Bar):
    @property
    def stuff(self):

        # Computes stuff from the internal state like Foo().stuff
        foo_stuff = get_stuff_from(Foo)

        # Computes stuff from the internal state like Bar().stuff
        bar_stuff = get_stuff_from(Bar)

        # Returns the combined results
        return foo_stuff + bar_stuff

foo_bar = FooBar()
print(foo_bar.stuff)

which should output:

['a', 'b', 'c', '1', '2', '3']

If stuff were a method instead of a property, this would be simple to implement:

class Foo():
    def stuff(self):
        return ['a','b','c']

class Bar():
    def stuff(self):
        return ['1','2','3']

class FooBar(Foo, Bar):
    def stuff(self):

        # Computes stuff from the internal state like Foo().stuff
        foo_stuff = Foo.stuff(self)

        # Computes stuff from the internal state like Bar().stuff
        bar_stuff = Bar.stuff(self)

        # Returns the combined results
        return foo_stuff + bar_stuff

foo_bar = FooBar()
print(foo_bar.stuff())

however, I would like to find out whether it is possible to do the same with properties.

Upvotes: 0

Views: 122

Answers (4)

chepner
chepner

Reputation: 530970

Having two parents using the same name for two different things is a conflict you should fix before inheriting. Usually, this means using at least one adaptor class.

class Foo():
    @property
    def stuff(self):
        return ['a','b','c']

class Bar():
    @property
    def stuff(self):
        return ['1','2','3']


class BarAdaptor:
    def __init__(self):
        self.b = Bar()

    @property
    def bar_stuff(self):
        return self.b.stuff


class FooBar(Foo, BarAdaptor):
    # Don't repeat or perpetuate the mistake of
    # overloading stuff to mean different things.
    @property
    def foobar_stuff(self):
        return self.stuff + self.bar_stuff

For symmetry, you might want to adapt Foo and Bar.

This is inspired by the advice for incorporating non-cooperative classes into a hierarchy of cooperative multiple inheritance classes, outlined in Python's `super() considered super!.

Upvotes: 1

Sidharth Mudgil
Sidharth Mudgil

Reputation: 1461

You can do this using the code below

class FooBar(Foo, Bar):
    def __init__(self):
        self.foo = Foo()
        self.bar = Bar()

    @property
    def stuff(self):
        return self.foo.stuff + self.bar.stuff

Upvotes: -1

pho
pho

Reputation: 25489

A property is just an object with an fget method. You could access the base class' property object and invoke its fget method with the self object that refers to the child class:

class FooBar(Foo, Bar):
    @property
    def stuff(self):

        # Computes stuff from the internal state like Foo().stuff
        foo_stuff = Foo.stuff.fget(self)

        # Computes stuff from the internal state like Bar().stuff
        bar_stuff = Bar.stuff.fget(self)

        # Returns the combined results
        return foo_stuff + bar_stuff

And now FooBar().stuff returns ['a', 'b', 'c', '1', '2', '3']

Upvotes: 1

Nice Zombies
Nice Zombies

Reputation: 1087

You could do it like this, otherwise I don't know why you're deriving from Foo and Bar. In your current code you were just overwriting these properties.

class Foo():
    @property
    def stuff(self):
        return self._foo_stuff
    @property
    def _foo_stuff(self):
        return ['a','b','c']
class Bar():
    @property
    def stuff(self):
        return self._bar_stuff
    @property
    def _bar_stuff(self):
        return ['1','2','3']
class FooBar(Foo, Bar):
    @property
    def stuff(self):
        return self._foo_bar_stuff
    @property
    def _foo_bar_stuff(self):
        return self._foo_stuff + self._bar_stuff
foo_bar = FooBar()
print(foo_bar.stuff)

Output:

['a', 'b', 'c', '1', '2', '3']

Upvotes: 0

Related Questions