Simple class inheritance in Python - python

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.

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.

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.

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())

Related

how to make a copy of a class in python?

I have a class A
class A(object):
a = 1
def __init__(self):
self.b = 10
def foo(self):
print type(self).a
print self.b
Then I want to create a class B, which equivalent as A but with different name and value of class member a:
This is what I have tried:
class A(object):
a = 1
def __init__(self):
self.b = 10
def foo(self):
print type(self).a
print self.b
A_dummy = type('A_dummy',(object,),{})
A_attrs = {attr:getattr(A,attr) for attr in dir(A) if (not attr in dir(A_dummy))}
B = type('B',(object,),A_attrs)
B.a = 2
a = A()
a.foo()
b = B()
b.foo()
However I got an Error:
File "test.py", line 31, in main
b.foo()
TypeError: unbound method foo() must be called with A instance as first argument (got nothing instead)
So How I can cope with this sort of jobs (create a copy of an exists class)? Maybe a meta class is needed? But What I prefer is just a function FooCopyClass, such that:
B = FooCopyClass('B',A)
A.a = 10
B.a = 100
print A.a # get 10 as output
print B.a # get 100 as output
In this case, modifying the class member of B won't influence the A, vice versa.
The problem you're encountering is that looking up a method attribute on a Python 2 class creates an unbound method, it doesn't return the underlying raw function (on Python 3, unbound methods are abolished, and what you're attempting would work just fine). You need to bypass the descriptor protocol machinery that converts from function to unbound method. The easiest way is to use vars to grab the class's attribute dictionary directly:
# Make copy of A's attributes
Bvars = vars(A).copy()
# Modify the desired attribute
Bvars['a'] = 2
# Construct the new class from it
B = type('B', (object,), Bvars)
Equivalently, you could copy and initialize B in one step, then reassign B.a after:
# Still need to copy; can't initialize from the proxy type vars(SOMECLASS)
# returns to protect the class internals
B = type('B', (object,), vars(A).copy())
B.a = 2
Or for slightly non-idiomatic one-liner fun:
B = type('B', (object,), dict(vars(A), a=2))
Either way, when you're done:
B().foo()
will output:
2
10
as expected.
You may be trying to (1) create copies of classes for some reason for some real app:
in that case, try using copy.deepcopy - it includes the mechanisms to copy classes. Just change the copy __name__ attribute afterwards if needed. Works both in Python 2 or Python 3.
(2) Trying to learn and understand about Python internal class organization: in that case, there is no reason to fight with Python 2, as some wrinkles there were fixed for Python 3.
In any case, if you try using dir for fetching a class attributes, you will end up with more than you want - as dir also retrieves the methods and attributes of all superclasses. So, even if your method is made to work (in Python 2 that means getting the .im_func attribute of retrieved unbound methods, to use as raw functions on creating a new class), your class would have more methods than the original one.
Actually, both in Python 2 and Python 3, copying a class __dict__ will suffice. If you want mutable objects that are class attributes not to be shared, you should resort again to deepcopy. In Python 3:
class A(object):
b = []
def foo(self):
print(self.b)
from copy import deepcopy
def copy_class(cls, new_name):
new_cls = type(new_name, cls.__bases__, deepcopy(A.__dict__))
new_cls.__name__ = new_name
return new_cls
In Python 2, it would work almost the same, but there is no convenient way to get the explicit bases of an existing class (i.e. __bases__ is not set). You can use __mro__ for the same effect. The only thing is that all ancestor classes are passed in a hardcoded order as bases of the new class, and in a complex hierarchy you could have differences between the behaviors of B descendants and A descendants if multiple-inheritance is used.

Grandchild inheriting from Parent class - Python

I am learning all about Python classes and I have a lot of ground to cover.
I came across an example that got me a bit confused.
These are the parent classes
Class X
Class Y
Class Z
Child classes are:
Class A (X,Y)
Class B (Y,Z)
Grandchild class is:
Class M (A,B,Z)
Doesn't Class M inherit Class Z through inheriting from Class B or what would the reason be for this type of structure? Class M would just ignore the second time Class Z is inherited wouldn't it be, or am I missing something?
Class M would just inherit the Class Z attributes twice (redundant) wouldn't it be, or am I missing something?
No, there are no "duplicated" attributes, Python performs a linearization they can the Method Resolution Order (MRO) as is, for instance, explained here. You are however correct that here adding Z to the list does not change anything.
They first construct MRO's for the parents, so:
MRO(X) = (X,object)
MRO(Y) = (Y,object)
MRO(Z) = (Z,object)
MRO(A) = (A,X,Y,object)
MRO(B) = (B,Y,Z,object)
and then they construct an MRO for M by merging:
MRO(M) = (M,)+merge((A,X,Y,object),(B,Y,Z,object),(Z,object))
= (M,A,X,B,Y,Z,object)
Now each time you call a method, Python will first check if the attribute is in the internal dictionary self.__dict__ of that object). If not, Python will walk throught the MRO and attempt to find an attribute with that name. From the moment it finds one, it will stop searching.
Finally super() is a proxy-object that does the same resolution, but starts in the MRO at the stage of the class. So in this case if you have:
class B:
def foo():
super().bar()
and you construct an object m = M() and call m.foo() then - given the foo() of B is called, super().bar will first attempt to find a bar in Y, if that fails, it will look for a bar in Z and finally in object.
Attributes are not inherited twice. If you add an attribute like:
self.qux = 1425
then it is simply added to the internal self.__dict__ dictionary of that object.
Stating Z explicitly however can be beneficial: if the designer of B is not sure whether Z is a real requirement. In that case you know for sure that Z will still be in the MRO if B is altered.
Apart from what #Willem has mentioned, I would like to add that, you are talking about multiple inheritance problem. For python, object instantiation is a bit different compared other languages like Java. Here, object instatiation is divided into two parts :- Object creation(using __new__ method) and object initialization(using __init__ method). Moreover, it's not necessary that child class will always have parent class's attributes. Child class get parent class's attribute, only if parent class constructor is invoked from child class(explicitly).
>>> class A(object):
def __init__(self):
self.a = 23
>>> class B(A):
def __init__(self):
self.b = 33
>>> class C(A):
def __init__(self):
self.c = 44
super(C, self).__init__()
>>> a = A()
>>> b = B()
>>> c = C()
>>> print (a.a) 23
>>> print (b.a) Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'B' object has no attribute 'a'
>>> print (c.a) 23
In the above code snipped, B is not invoking A's __init__ method and so, it doesn't have a as member variable, despite the fact that it's inheriting from A class. Same thing is not the case for language like Java, where there's a fixed template of attributes, that a class will have. This's how python is different from other languages.
Attributes that an object have, are stored in __dict__ member of object and it's __getattribute__ magic method in object class, which implements attribute lookup according to mro specified by willem. You can use vars() and dir() method for introspection of instance.

Using variable value for creating class instance

Is there a way in Python, that i can use the variable value for creation of Class instance
class Test(object):
def __init__(self, object):
self.name = object
print "Created Object :",self.name
a1 = Test('a1')
print a1.name
b2 = 'a2'
b2 = Test(b2)
print a2.name
In this example, i want the class instance name should be 'a1' and 'a2' respectively. I cannot directly use the value 'a2' as it is computed from some other process, but the class instance name must match it such that it can be accessed.
In the above example, it gives error :
Created Object : a1
a1
Created Object : a2
Traceback (most recent call last):
File "D:\a\h", line 12, in <module>
print a2.name
NameError: name 'a2' is not defined
Any time you want to dynamically create variable names, you need to stop and tell yourself that a dictionary is a better way to go. If you want the class instance to be available anywhere (which is also not the best idea), but here's how you can do that:
class Test(object):
instance_dict={}
def __init__(self,name):
self.name=name
self.instance_dict[name] = self
Now you can use this class:
a1 = Test("a1")
print a1 is Test.instance_dict["a1"] #True
b1 = Test("a2")
print b1 is Test.instance_dict["a2"] #True
In other words, the instance_dict class attribute keeps a handle on the most recently created instance with a particular name so you have a handle on the instances anywhere that Test is available.
However, I do not recommend this type of design. The downsides here are the same as the downsides with using global variables. It will probably become difficult to maintain, hard to tell what exactly is going on, etc. simply because the flow of data through your program is not well ordered.
The closest thing to what you are looking for is to store all your instances in a dictionary:
class Test(object):
def __init__(self, n):
self.name = n
print "Created Object :",self.name
d = {}
d['a1'] = Test('a1')
print d['a1'].name
b2 = 'a2'
d[b2] = Test(b2)
print d['a2'].name
There is no connection between the name of a variable that references an object and the object itself.
Try print b2.name instead.
Note: The name name doesn't mean anything special to Python. "Giving your class a name" means nothing to Python, it just executes the code that you write. "Giving your class a name" means something to you but Python can't and won't read your mind :-)
So setting the name of the instance referenced by b2 to a2 doesn't magically create a reference a2.
This introduction to variable names might help: Learn to Program Using Python: Variables and Identifiers
In a nutshell, if you write
a = 'x'
Python
Creates a string instance
Assigns a value to that string instance (the text x)
Assigns a reference to this new string instance to the alias a
If you then write:
b = a
there is still only a single string instance in memory. You only created a second alias b which references the same string instance as a.

Python inheritance

My base code looks like this:
class C1(object):
def f(self):
return 2*self.g()
def g(self):
return 2
class C2(C1):
def f(self):
return 3*self.g()
class C3(C1):
def g(self):
return 5
class C4(C3):
def f(self):
return 7*self.g()
obj1 = C1()
obj2 = C2()
obj3 = C3()
obj4 = C4()
Now my question is the following: I need to write three assignment statements that do the following:
assign the calling list for obj2.f() to the variable obj2_calls
assign the calling list for obj3.f() to the variable obj3_calls
assign the calling list for obj4.f() to the variable obj4_calls
Calling list being for example, when obj1.f() is called, the f method of C1 is called which calls the g method of C1. This could be represented as a calling list of the form ['C1.f', 'C1.g']
I don't quite know the proper way to write the assignment statements and I desperately want to help out my friend with her stuff.
If you could just show me how to properly right out the first assignment statement, I'm sure I could figure out the rest.
The key insight is that if a method is not defined for a class, it will default to use the method of a class that the class inherits.
Thus, ask yourself what 'obj2.f()' will do.
What class is obj2? It is C2.
Is there an f method defined for class C2? Yes, there is. So the method C2.f is called.
The C2.f method calls self.g, which means it looks for C2.g. Is there a 'C2.g' method? No, so we have to go to the class that C2 inherits from. The line class C2(C1) tells us that it is inherited from the class C1, so it will call the method C1.g.
Those are the steps to get the first calling list; the rest are up to you.

python: confusion with local class name

I have the following code:
def f():
class XYZ:
# ...
cls = type('XXX', (XYZ, ), {})
# ...
return cls
I am now using it as follows:
C1 = f()
C2 = f()
and it seems to work fine: C1 is C2 returns False, there's no conflict between the class attributes of the two classes, etc.
Question 1
Why is that? How is it possible that C1 and C2 are both shown as class
<'__main__.XXX'>
and yet not the same class?
Question 2
Is there some problem with the fact that I have two identical names for two different classes?
Question 3
I would like to be able to write instead:
f('C1')
f('C2')
with the same effect. Is it possible?
Question 4
If I want C1 to look like a regular class, not main.XXX, is it ok to say:
C1.__name__ = '__main__.C1'
Question 3
To have cls.__name__ be anything you want, (with a nod to delnan's suggestion)
def f(clsname):
class XYZ:
# ...
XYZ.__name__ = XYZ
# ...
return XYZ
Question 1
The reason that c1 is not c2 is that they are two different objects stored at two different locations in memory.
Question 4
Try an answer to question 1 and see how it works out for you
Question 2
It can complicate debugging that their class attributes __name__ share a common value and this is bad enough to take pains to avoid. (see question 3). I would maintain though that they don't have the same name. One is named C1 and the other is named C2 (at least in the scope you are showing. If you were to pass them to a function, then there name in that scope would be the same as the name of parameter that they were passed through)
In fact, I'm so sure that they don't have the same name that trying to tell me otherwise is likely to cause me to turn the music up louder and pretend I can't hear you.
In response to comment
It can be done but it's just wrong. I'll illustrate anyway because it's illuminating:
def f(clsname):
class XYZ(object):
pass
XYZ.__name__ = clsname
globals()[clsname] = XYZ
f('C1')
f('C2')
print C1
print C2
This just works by sticking the class in the globals dict keyed by clsname. But what's the point? You can stick it in the globals dict under any name in fact because this is just another assignment. You are best off just returning the class from the function and letting the caller decide what name to give the class in it's own scope. You still have the __name__ attribute of the class set to the string you pass to the function for debugging purposes.
Actually, you don't need to the cls = ... line at all.
>>> def f():
... class C:
... pass
... return C
...
>>> f() is f()
False
Reason: class (as well as e.g. def) defines a new class each time it is encountered = each time the function is called.
As for cls.__name__, it's really no semantic difference. The name is useful for debugging (you don't expose it directly to the user, do you?) and introspection, but it shouldn't be an issue. But if you absolutely want to have different names, you can change cls.__name__ before returning (also note that after C.__name__ = 'foo', C.__name__ == '__main__.foo'!).
At question 3: It would be possible to inject it directly into global namespace... don't do this. It has no advantages, only disatvantages: nonobvious side effects, bad style, the fact it's a hack at all, etc!

Categories

Resources