Importance of Inheritance in python - python

I have implemented inheritance in python and code is working, Also, I have tried doing the same thing in other way also, I want to know where inheritance has advantage over my second way of doing the same thing.
class A:
def __init__(self,a,b):
self._a = a
self.b = b
def addition(self):
return self._a+self.b
################################### Inheritance ############
class B(A):
def __init__(self, a,b):
super().__init__(a,b)
print("Protected in B ",self._a)
################################### Doing same work by creating object #################
class D():
def __init__(self, a,b):
self.a = a
self.b = b
d = A(self.a,self.b)
print(d.addition())
print("Protected in D ", d._a)
I'm able to call protected member of class A in both ways. Any member of A can be accessed in both ways, so, why inheritance is importance. In which scenario only inheritance will work and NOT object creation ?

I don't believe that your example showcases the power of inheritance.
The 2nd example doesn't look very OOP-like. I rarely see object creation in the constructor unless it is an instance variable or a static variable.
Not every base class will have all the fields that the subclass has. For example, an Animal class may be super generic, but as more classes like Tiger extends it, Tiger will have more tiger properties (and not have the basic same ones as the Animal class).
To focus back on your question, the 2nd example, will require you to use self.d if you want to access the protected variable _a, unless you want to do d = D() and then d.a, which isn't very protected. You are creating an object within D, whereas you are still setting up the blueprint for the class in the 1st example. That might not be what you are looking to do, as the 2nd example with the object creation looks like it could be the driver class.

Related

Pythonic way to override a method from a parent class for any child class

I have a base class with some methods and some (potentially many) child classes which modify some aspects of the base class. Additionally, there is a common modification that I'd like to be able to apply to any of the child classes. This modification involves overriding a method. I feel like there should be a simple OOP approach to achieving this, but I don't know what it is.
Say I have a parent class (Base) and some child classes (A and B):
class Base:
def __init__(self):
self.x = 0
def go(self):
self.x+=1
def printme(self):
print(f'x: {self.x}')
class A(Base): #subclasses Base
def go(self):
self.x-=1
class B(Base): #subclasses Base
def go(self):
self.x-=2
a = A()
b = B()
I want to be able to update the .printme() method of objects like a or b
but this doesn't work
def printme(self):
print(f'modified printing of x: {self.x}')
a.printme = printme
a.printme() #raises TypeError: printme() missing 1 required positional argument: 'self'
(I've seen this solution but it seems hacky and I think there must be a better way.)
I also can't think of a way to use multiple inheritance: if I write a single subclass of Base with the updated printme() function, then I can't generate objects that have the go() methods of class A or B without writing separate subclasses for each of those.
After thinking about it for a while longer, I realized it would be possible to dynamically create a new subclass from any child class and override the method like this:
def with_mod(Class):
class ClassWithMod(Class):
def printme(self):
print(f'new statement: {self.x}')
return ClassWithMod
a = A()
a.printme() #prints "x: 0"
a_mod = with_mod(A)()
a_mod.printme() #prints "new statement: 0"
I tested this code and it works, but is this the "correct" approach or is there a more correct/pythonic OOP approach? What I don't like about this solution is that type(a_mod) is the generic ClassWithMod. I'd like it to still be of type A. (Maybe this means I want to override the method at the instance level rather than the class level?)

Initialize common variables used in mutiple classes Python

Assuming that I have two classes A and B. In class A, B's instances will created. Both of their instance methods will use a common variable, which is initialized in class A. I have to pass the common_var through the init function. Think that if I have classes A, B, C, D.... and common_var1, var2, var3...where all vars have to be passed through class to class, that’s terrible:
class A:
def __init__(self, variable_part):
self.common_var = "Fixed part" + variable_part
self.bList = []
def add_B(self):
self.bList += [B(self.common_var)]
def use_common_var():
do_something(self.common_var)
class B:
def __init__(self, common_var):
self.common_var = common_var
def use_common_var():
do_something(self.common_var)
There's an ugly approach to use global here:
class A:
def __init__(self, variable_part):
global common_var
common_var = "Fixed part" + variable_part
def use_common_var(self):
do_something(common_var)
class B:
def use_common_var(self):
do_something(common_var)
But I don't think it's a good idea, any better ideas?
Update:
The original question is here:
The common_vars are a series of prefixes of strings, things like "https://{}:{}/rest/v1.5/".format(host, port), "mytest" etc..
and in class A, I use
"https://127.0.0.1:8080/rest/v1.5/interface01" and "mytest_a"
in class B, I use
"https://127.0.0.1:8080/rest/v1.5/interface02" and "mytest_b"
in class C, I may use
"https://127.0.0.1:8080/rest/v1.5/get?name=123" and "mytest_c"
things like that, I use common variables just to multiplex 'https://{}:{}/rest/v1.5' and "mytest" part and none of these A, B, C classes is in "is-a" relationship. But the core part of this problem is the common_var is not common at very first but initialized in one of this class..
Final Update
I compromised. I added a Helper class to reuse the common values:
class Helper:
#staticmethod
def setup(url, prefix):
Helper.COMMON_URL = url
Helper.prefix = prefix
# A always initiates first
Class A:
def __init__(self, host, port):
Helper.setup(
"https://{0}:{1}/rest/v1.5".format(host, port),
"test"
)
def use_common_var():
do_something(Helper.url, Helper.prefix)
class B:
def use_common_var():
do_somesthing(Helper.url, Helper.prefix)
class C:
def use_common_var():
do_something(Helper.url, Helper.prefix)
Is this a better way?
If you have four classes that share the same set of four attributes, then you might (or not) have a use for inheritance - but this really depends on how/whatfor those attributes are used and what's the real relationship between those classes are. Inheritance is a "is a" relationship (if B inherits from A then B "is a" A too).
Another solution - if you don't really have a "is a" relationship - is to create a class that regroup those four attributes and pass an instance of this class where it's needed. Here again, it only makes sense if there is a real semantic relationshop between those attributes.
Oh and yes, using globals is usually not the solution (unless those variables are actually pseudo-constants - set once at startup and never changed by anyone).
To make a long story short: there's no one-size-fits-all answer to your question, the appropriate solution depends on your concrete use case.
EDIT:
given your added explanation, it looks lik the "cargo class" solution (grouping all your "shared" attributes in a same object) might be what you want. Just beware that it will couple all your classes to this class, which might or not be a problem (wrt/ testing and maintainability). If the forces that drive your A / B ∕ C /D classes evolution are mostly the same then you should be good...

Python class forward declaration [duplicate]

I have a series of Python classes in a file. Some classes reference others.
My code is something like this:
class A():
pass
class B():
c = C()
class C():
pass
Trying to run that, I get NameError: name 'C' is not defined. Fair enough, but is there any way to make it work, or do I have to manually re-order my classes to accommodate? In C++, I can create a class prototype. Does Python have an equivalent?
(I'm actually playing with Django models, but I tried not complicate matters).
Actually, all of the above are great observations about Python, but none of them will solve your problem.
Django needs to introspect stuff.
The right way to do what you want is the following:
class Car(models.Model):
manufacturer = models.ForeignKey('Manufacturer')
# ...
class Manufacturer(models.Model):
# ...
Note the use of the class name as a string rather than the literal class reference. Django offers this alternative to deal with exactly the problem that Python doesn't provide forward declarations.
This question reminds me of the classic support question that you should always ask any customer with an issue: "What are you really trying to do?"
In Python you don't create a prototype per se, but you do need to understand the difference between "class attributes" and instance-level attributes. In the example you've shown above, you are declaring a class attribute on class B, not an instance-level attribute.
This is what you are looking for:
class B():
def __init__(self):
self.c = C()
This would solve your problem as presented (but I think you are really looking for an instance attribute as jholloway7 responded):
class A:
pass
class B:
pass
class C:
pass
B.c = C()
Python doesn't have prototypes or Ruby-style open classes. But if you really need them, you can write a metaclass that overloads new so that it does a lookup in the current namespace to see if the class already exists, and if it does returns the existing type object rather than creating a new one. I did something like this on a ORM I write a while back and it's worked very well.
A decade after the question is asked, I have encountered the same problem. While people suggest that the referencing should be done inside the init method, there are times when you need to access the data as a "class attribute" before the class is actually instantiated. For that reason, I have come up with a simple solution using a descriptor.
class A():
pass
class B():
class D(object):
def __init__(self):
self.c = None
def __get__(self, instance, owner):
if not self.c:
self.c = C()
return self.c
c = D()
class C():
pass
>>> B.c
>>> <__main__.C object at 0x10cc385f8>
All correct answers about class vs instance attributes. However, the reason you have an error is just the order of defining your classes. Of course class C has not yet been defined (as class-level code is executed immediately on import):
class A():
pass
class C():
pass
class B():
c = C()
Will work.

python: changing object class in multi-level inheritance

I have looked at many questions posted here to find an answer to my problem, but I wasn't successful. The problem might be, that I just don't know for what keywords I should look. So my problem is the following:
I've got a program, that has a multi-level inheritance and I am trying to figure out how the best way would be to change the class of an object to a subclass. Let's say I have the following code:
class A(object):
def __init(self, filename, ..)
super(A, self).__init__()
...some assignments here
class B(A):
def __init(self, filename, ..)
super(B, self).__init__()
...some assignments here
class C(A):
def __init(self, filename, ..)
super(C, self).__init__()
...some assignments here
and so on...
I always want to start initialising an object of class A. Depending on the type of the file that is used, the assignments may differ and depending on those assignments I can determine what kind of file it is. So now I want to change the class of the object to whatever class is suitable..
I know I could pass the A object to B or C and use copy or deepcopy, but in A i am assigning an object of which the reference should not change and some others where it should change. Also I would need to delete that object of A, after initialising B or C.
class B(A):
def __init__(self, filename, objA = None):
if objA is not None:
self.__dict__ = copy.deepcopy(objA.__dict__)
del(objA)
else:
super(B, self).__init__(filename)
Also there is another possibility by changing the _class attribute to another class and use some kind of update method of the new class.
I would like to know, which of the two approaches is recommended or is there even a better one. Thanks in advance.
What you want is a factory: a function that opens the file, reads the stuff it needs to read to figure out what kind of file it is, and then initializes and returns an object of the appropriate class.
If you want to keep it a class, you'd want to override __new__() and then return an object of the desired class instead of its own class. (You could also do it using a metaclass and overriding __call__() on that.)
You can change an instance's class after instantiating it as well, by changing its __class__ attribute to point to the desired class. That'll work, but the factory is going to be more familiar to other programmers who will read your code.
This code will explain what you want to do
class B(object):
def x(self):
print 'b'
class A(object):
def x(self):
print 'a'
Now we create two objects
a = a()
b = b()
a.x()
a
b.x()
b
now if you want 'a' to become a B object
a.__class__ = type(b)
or
a.__class__ = B
now the x attribute is from the B class.
a.x()
b

Does Python have class prototypes (or forward declarations)?

I have a series of Python classes in a file. Some classes reference others.
My code is something like this:
class A():
pass
class B():
c = C()
class C():
pass
Trying to run that, I get NameError: name 'C' is not defined. Fair enough, but is there any way to make it work, or do I have to manually re-order my classes to accommodate? In C++, I can create a class prototype. Does Python have an equivalent?
(I'm actually playing with Django models, but I tried not complicate matters).
Actually, all of the above are great observations about Python, but none of them will solve your problem.
Django needs to introspect stuff.
The right way to do what you want is the following:
class Car(models.Model):
manufacturer = models.ForeignKey('Manufacturer')
# ...
class Manufacturer(models.Model):
# ...
Note the use of the class name as a string rather than the literal class reference. Django offers this alternative to deal with exactly the problem that Python doesn't provide forward declarations.
This question reminds me of the classic support question that you should always ask any customer with an issue: "What are you really trying to do?"
In Python you don't create a prototype per se, but you do need to understand the difference between "class attributes" and instance-level attributes. In the example you've shown above, you are declaring a class attribute on class B, not an instance-level attribute.
This is what you are looking for:
class B():
def __init__(self):
self.c = C()
This would solve your problem as presented (but I think you are really looking for an instance attribute as jholloway7 responded):
class A:
pass
class B:
pass
class C:
pass
B.c = C()
Python doesn't have prototypes or Ruby-style open classes. But if you really need them, you can write a metaclass that overloads new so that it does a lookup in the current namespace to see if the class already exists, and if it does returns the existing type object rather than creating a new one. I did something like this on a ORM I write a while back and it's worked very well.
A decade after the question is asked, I have encountered the same problem. While people suggest that the referencing should be done inside the init method, there are times when you need to access the data as a "class attribute" before the class is actually instantiated. For that reason, I have come up with a simple solution using a descriptor.
class A():
pass
class B():
class D(object):
def __init__(self):
self.c = None
def __get__(self, instance, owner):
if not self.c:
self.c = C()
return self.c
c = D()
class C():
pass
>>> B.c
>>> <__main__.C object at 0x10cc385f8>
All correct answers about class vs instance attributes. However, the reason you have an error is just the order of defining your classes. Of course class C has not yet been defined (as class-level code is executed immediately on import):
class A():
pass
class C():
pass
class B():
c = C()
Will work.

Categories