0

I have code like this:

class X(object):
    def __init__(self):
        print('X')
    def Print(self):
        print('X')


class Y(object):
    def __init__(self):
        print('Y')
    def Print(self):
        print('Y')

class Z(X,Y):
    def __init__(self):
        print('Z')
        def Print(self):
            print('z')
            super().Print()

>>> z=Z()
Z
>>> z.Print()
X

It searches for Print according to

Z.__mro__
(<class '__main__.Z'>, <class '__main__.X'>, <class '__main__.Y'>, <class 'object'>)

and find it for first time in X. But if I want to z.Print() run Y.Print(), I can use an explicit class name like:

class Z(X,Y):
    def __init__(self):
        print('Z')
        def Print(self):
            print('z')
            Y.Print()

but this is not dynamic. Is there a better way to do this?

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Zero Days
  • 851
  • 1
  • 11
  • 22
  • Well, you could define Z as `class Z(Y, X)` if you want the classes to be inherited in the other order. Is that what you're asking? – BrenBarn Sep 27 '15 at 06:54
  • no ,I want to do this with out changing the inheritance orders – Zero Days Sep 27 '15 at 06:55
  • 5
    And how do you expect Python to guess which one you want to use if you insist on defining them out of order? You could always do `self.__class__.__bases__[1].Print()` if you always want to pick the 2nd parent class, but if you do, remind me never to work with you.. – mbinette Sep 27 '15 at 06:59
  • 1
    @ZeroDays: In what sense do you want it to be "dynamic"? You can either use the inheritance order, or you can explicitly say which class you want to use, but there aren't really any other options that make sense. How do you expect Python to know which one you want to use? – BrenBarn Sep 27 '15 at 07:01
  • ok. so the beast way here is use explicit class name ? – Zero Days Sep 27 '15 at 07:03
  • 3
    If you want to preserve the inheritance order (that is, have all of X's methods take priority over Y's), except for that one call, then yes, I would say using Y explicitly for that call is your best option. – mbinette Sep 27 '15 at 07:04

2 Answers2

2

I really depends what you are trying to do. If you want to make sure both X.Print and Y.Print are called then you need to add super calls in both X.Print and Y.Print, and an a base class with a place holder Print method.

If you want to call X.Print or Y.Print depending on some criteria, then inheritance may be the wrong model for you. You may wish to try using composition. This is where you write a class that does not inherit from X or Y, but has instances of them as members and knows how to use them. eg.

Inheritance

from abc import abstractmethod, ABCMeta

class Base(metaclass=ABCMeta):
    @abstractmethod
    def Print(self):
        pass

class X(Base):
    def Print(self):
        print("X")
        super().Print()

class Y(Base):
    def Print(self):
        print("Y")
        super().Print()

class Inheritance(X, Y):
    def Print(self):
        print("Inheiritance")
        super().Print()

Inheritance().Print()

Outputs:

Inheiritance
X
Y

Composition

class Composition:
    def __init__(self):
        self.x = X()
        self.y = Y()
    def Print(self):
        print("Composition")
        self.x.Print()
        self.y.Print()

Composition().Print()

Outputs:

Composition
X
Y
Dunes
  • 37,291
  • 7
  • 81
  • 97
  • Without knowing what X, Y and Z are it's though to really know what model applies, but I concur on the composition advice. Composition over Inheritance (https://en.wikipedia.org/wiki/Composition_over_inheritance). – mbinette Sep 27 '15 at 14:33
1

For future reference purposes, here is a summary of the options that were discussed in the comments.

1. Change the order of inheritance

class Z(Y, X):
    ...

That would ensure that Y's methods are called over X's methods when using super, including the Print method.

2. Explicitly call Y's Print method

class Z(X, Y):
    ...
    def Print(self):
        Y.Print(self)

That would ensure that X's methods are called over Y's methods when using super, except for that one call, which would explicitly call Y's Print.

3. (Do not use) Explicitly call the second parent class's method

class Z(X, Y):
    ...
    def Print(self):
        self.__class__.__bases__[1].Print()

That would ensure that X's methods are called over Y's methods when using super, except for that one call, which would explicitly call the second parent class's Print (in this case, Y).

mbinette
  • 5,094
  • 3
  • 24
  • 32