4

I am fairly new to Python and OOP. Suppose I have two classes, Base1 and Base2. Suppose also that Base1 has computed some values a1, b1 and that Base2 has a method that multiplies two values. My question is, what is the correct way of passing a1 and b1 of Base1 to multiply in Base2?

One way to do this by defining a derived class, Derived as follows:

class Base1:
    def __init__(self, x1 , y1):
        # perform some computation on x1 and y1
        # store result in a1 and b1
        self.a1=x1
        self.b1=y1

class Base2:
    def __init__(self, x2 , y2):
        self.a2=x2
        self.b2=y2
        self.c2=self.multiply(self.a1,self.b1) # using a1 and b1 of Base1

    def multiply(self, p,q):
        return p*q

class Derived(Base1,Base2):
    def __init__(self):
        self.describe='Derived Class'
        Base1.__init__(self,3,4)
        Base2.__init__(self,5,6)

Then:

f=Derived()
f.c2=12

However, in a more complex situation, it is easy to lose track of where self.a1, self.b1 came from. It is also not obvious to me why the two base classes can access the attributes and the methods of each other in this way?

Edit: This is Python 2.7.10.

10
  • Use "private" variables like __a1. Commented Feb 20, 2016 at 9:17
  • there is nothing like private in python Commented Feb 20, 2016 at 9:18
  • If this is 2.x Python, you need to use object. Also, you should read up on super. As it stands, Base2.__init__ will never be executed. Commented Feb 20, 2016 at 9:19
  • You're not adding any value, @Shadowfax. I intentionally quoted private because it isn't private in the usual sense. Would you like to add a reference to Python's handling of variables starting with double underscores? Commented Feb 20, 2016 at 9:21
  • @UlrichEckhardt Oops, I missed that! Commented Feb 20, 2016 at 9:25

3 Answers 3

2

In Python 2 always inherit from object. Otherwise you get old-style classes which should not use:

class Base1(object):
    def __init__(self, x1 , y1):
        # perform some computation on x1 and y1
        # store result in a1 and b1
        self.a1 = x1
        self.b1 = y1

class Base2(object):
    def __init__(self, x2 , y2):
        self.a2 = x2
        self.b2 = y2
        self.c2 = self.multiply(self.a1, self.b1) # using a1 and b1 of Base1

    def multiply(self, p,q):
        return p*q

class Derived(Base1, Base2):
    def __init__(self):
        self.describe='Derived Class'
        Base1.__init__(self, 3, 4)
        Base2.__init__(self, 5, 6)

Python looks for methods using the method resolution order (mro). You can find out the current order:

>>> Derived.mro()
[__main__.Derived, __main__.Base1, __main__.Base2, object]

That means Python looks for a method multiply() in the class Derived first. If it finds it there, it will use it. Otherwise it keeps searching using the mro until it finds it. Try changing the order of Base1 and Base2 in Derived(Base1,Base2) and check how this effects the mro:

class Derived2(Base2, Base1):
    pass


>>> Derived2.mro()
[__main__.Derived2, __main__.Base2, __main__.Base1, object]

The self always refers to the instance. In this case f (f = Derived()). It does not matter how f gets its attributes. The assignment self.x = something can happen in any method of any of the classes involved.

Sign up to request clarification or add additional context in comments.

Comments

1

TL;DR

Python is dynamic. It doesn't check if attributes are present until the actual line of code that tries to access them. So your code happens to work. Just because you can do this, doesn't mean you should, though. Python depends on you to make good decisions in organizing your code rather than trying to protect you from doing dumb things; we're all adults here.

Why you can access variables

The reason really boils down to the fact that Python is a dynamic language. No types are assigned to variables, so Python doesn't know ahead of time what to expect in that variable. Alongside that design decision, Python doesn't actually check for the existence of an attribute until it actually tries to access the attribute.

Let's modify Base2 a little bit to get some clarity. First, make Base1 and Base2 inherit from object. (That's necessary so we can tell what types we're actually dealing with.) Then add the following prints to Base2:

class Base2(object):
    def __init__(self, x2 , y2):
        print type(self)
        print id(self)
        self.a2=x2
        self.b2=y2
        self.c2=self.multiply(self.a1,self.b1) # using a1 and b1 of Base1

    def multiply(self, p,q):
        return p*q

Now let's try it out:

>>> d = Derived()
<class '__main__.Derived'>
42223600
>>> print id(d)
42223600

So we can see that even in Base2's initializer, Python knows that self contains a Derived instance. Because Python uses duck typing, it doesn't check ahead of time whether self has a1 or b1 attributes; it just tries to access them. If they are there, it works. If they are not, it throws an error. You can see this by instantiating an instance of Base2 directly:

>>> Base2(1, 2)
<class '__main__.Base2'>
41403888
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 7, in __init__
AttributeError: 'Base2' object has no attribute 'a1'

Note that even with the error, it still executes the print statements before trying to access a1. Python doesn't check that the attribute is there until the line of code is executed.

We can get even crazier and add attributes to objects as the code runs:

>>> b = Base1(1,2)
>>> b.a1
1
>>> b.notyet
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Base1' object has no attribute 'notyet'
>>> b.notyet = 'adding an attribute'
>>> b.notyet
'adding an attribute'

How you should organize this code

Base2 should not try to access those variables without inheriting from Base1. Even though it's possible to do this if you only ever instantiate instances of Derived, you should assume that someone else might use Base2 directly or create a different class that inherits from Base2 and not Base1. In other words, you should just ignore that this is possible. A lot of things are like that in Python. It doesn't restrict you from doing them, but you shouldn't do them because they will confuse you or other people or cause problems later. Python is known for not trying to restrict functionality and depending on you, the developer, to use the language wisely. The community has a catchphrase for that approach: we're all adults here.

I'm going to assume that Base2 is primarily intended to be just a mix-in to provide the multiply method. In that case, we should define c2 on the subclass, Derived, since it will have access to both multiply and the attributes a1 and b1.

For a purely derived value, you should use a property:

class Derived(Base1,Base2):
    def __init__(self):
        self.describe='Derived Class'
        Base1.__init__(self,3,4)
        Base2.__init__(self,5,6)

    @property
    def c2(self):
        return self.multiply(self.a1,self.b1) # using a1 and b1 of Base1

This prevents callers from changing the value (unless you explicitly create a setter) and avoids the issue of tracking where it came from. It will always be computed on the fly, even though using it looks like just using a normal attribute:

x = Derived()
print x.c2

This would give 12 as expected.

3 Comments

jpmc26, many thanks for the excellent answer. Assuming, however that Base2 is primarly intended to be just a mix-in is not accurate. In general, I am assuming Base2 will take some output of Base1, say noisy signal, and perform some complex filtering etc.
@Abbas In general, if you have computed things, it's better to compute them on the fly or cache them in a place that has access to all the data necessary. Computing on the fly when you can has major advantages in terms of giving you less to think about keeping organized; if you have to cache it, you need to make sure that you recalculate it anytime some of the inputs change, which is a lot more work keeping it organized in your head and your code. I would still try to keep that state on Derived for that reason; Base2 will never have any idea when the state changes.
You might also consider composition over inheritance. You might do just as well to have an instance of Base2 stored in an attribute on Derived instead of inheriting from it. In the absolute worst case where you can't change how the classes are organized, you should at least pass the needed values into Base2's initializer instead of referencing them directly. (Frankly, over the last few years, I've developed a strong distaste for Object Oriented code. I believe it's very rarely more useful than a more functional minded approach.)
0

You can just provide a method multiply in the base class which assumes that a1 and b1 has been defined in the base class. So the code will be like

class Base1(object):
    def __init__(self,a1,b1):
        self.a1 = a1
        self.b1 = b1

class Base2(Base1):

    def multiply():
        return self.a1*self.b1

Here as you havent provided a __init__ for base2 it will use the init method of base1 which takes in parameters as a1 and a2

so now

base = Base2(5,4)
print(base.multiply())

1 Comment

In general, Base2 is more complex, it will take some output of Base1, say noisy signal, and perform some complex filtering etc.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.