Avoid using child method in parent class in python - python

Suppose I have two classes in Python as below:
class Parent(object):
def __init__(self):
self.num = 0
def fun1(self):
print 'p.fun1'
def fun2(self):
self.fun1()
print 'p.fun2'
and
from parent import Parent
class Child(Parent):
def __init__(self):
super(Child,self).__init__()
def fun1(self):
print 'c.fun1'
def fun2(self):
super(Child, self).fun2()
print 'c.fun2'
and if I call fun2 of Child
from child import Child
test = Child()
test.fun2()
I get output:
c.fun1
p.fun2
c.fun2
Which means the call of Child.fun2() leads to Parent.fun2(). But inside the Parent.fun2(), I use self.fun1() which in this case interpreted as Child.fun1() in my test.
But I really want the class Parent to be individual and the call of Parent.fun2() always uses Parent.fun1() inside it.
How can I avoid this?
I only know that I can make Parent.fun1() private into Parent.__fun1(). But I also have some instances of Parent where I need to use Parent.fun1() outside this class. That means I really need to override the fun1().

That's how inheritance is supposed to work. For the behavior you need, you might want to reconsider Parent & Child class's relationship, or change the method names or at least make the Parent methods classmethods or staticmethods.
This should work for your need, but I don't really like it.
class Parent(object):
def __init__(self):
self.num=0
def fun1(self):
print 'p.fun1'
def fun2(self):
Parent.fun1(self)
print 'p.fun2'
Child class can remain the same.
In all classes accessed in the inheritance chain, self will always point to the instance of class actually instantiated and not the current class accessed in super call (or in order to find method/attribute for that matter). So self.fun2 will always point to the method of Child class.

There is a mechanism called Name Mangling:
Any identifier of the form __spam (at least two leading underscores, at most one trailing underscore) is textually replaced with _classname__spam, where classname is the current class name with leading underscore(s) stripped. This mangling is done without regard to the syntactic position of the identifier, as long as it occurs within the definition of a class.
Name mangling is helpful for letting subclasses override methods without breaking intraclass method calls
Python Classes Documentation
This should work:
class Parent(object):
def __init__(self):
self.num = 0
def fun1(self):
print 'p.fun1'
def fun2(self):
self.__fun1()
print 'p.fun2'
__fun1 = fun1

Related

Use a class variable across inheritance in Python

In Python, I want to define a top level class that can depend on a class variable. Then I want to be able to change that variable at the class level, for children of the class, but still inherit the functionality that uses that variable.
In general, my Parent class has some functions that depend on configuration variables. All my child classes use those same functions, but with different parameters. I would like to be able to change the parameters at the class level.
As the simplest example, here are two classes where the Parent defines functions in terms of my_global, then the Child attempts to change that variable (but fails)
class Parent():
my_global = "parent"
def _init_(self):
pass
def printmg(self):
print(Parent.my_global)
class Child(Parent):
my_global = "child"
my_parent = Parent()
my_parent.printmg()
my_child = Child()
my_child.printmg()
This outputs
parent
parent
While I would like it to output
parent
child
I don't wan't to keep the variables at the object level (i.e. self.my_global = "child"), or to rewrite the function for the child.
Change the line print(Parent.my_global) to print(self.my_global).
The self operater represents the current class. So printing like this will work.
If you don't need an instance method define printmg as classmethod:
#classmethod
def printmg(cls):
print(cls.my_global)

Understanding difference between parent class init() and super.__init__

I looked at answers https://stackoverflow.com/a/33469090/11638153 and https://stackoverflow.com/a/27134600 but did not understand what is "indirection." To be specific, if I have classes like below, are there any drawbacks or advantages in hardcoding the parent class __init__(), as done in class Child1 (why do we need super() when one can explicitly write __init__() methods to invoke)?
class Base1:
def __init__(self):
print("Class Base1 init()")
class Base2:
def __init__(self):
print("Class Base2 init()")
class Child1(Base1, Base2):
def __init__(self):
print("Class Child1 init()")
Base1.__init__(self)
Base2.__init__(self)
class Child2(Base1, Base2):
def __init__(self):
print("Class Child2 init()")
super().__init__()
if __name__ == "__main__":
obj1 = Child1()
print("---")
obj2 = Child2()
Roughly, the difference is that ParentClass.__init__(self) always calls the specifically named class while super().__init__() makes a runtime computation to determine which class to call.
In many situations, the two ways give the same results. However, super() is much more powerful. It a multiple inheritance scenario, it can potentially call some class other than the parent of the current class. This article covers the latter scenario is detail.
For single inheritance, the reason to prefer super() is that that it read nicely and that it makes maintenance easier by computing the parent class.
For multiple inheritance, super() is the only straightforward way to access parent classes in the order of the MRO for the class of the calling instance. If those words don't mean anything to you, please look at the referenced article.
In your example code, Child1's design is very fragile. If its two base classes share some common base (and call their own parents' __init__ method inside of their own turn), that grandparent class might get initialized more than once, which would be bad if it's computationally costly, or has side effects. This is the "diamond pattern" of inheritance hierarchy that can be troublesome in many languages (like C++) that allow multiple inheritance.
Here's an example of the bad problem that can come up with explicit parent calls, and which super() is designed to fix:
class Grandparent:
def __init__(self):
print("Grandparent")
class Parent1(Grandparent):
def __init__(self):
print("Parent1")
Grandparent.__init__(self)
class Parent2(Grandparent):
def __init__(self):
print("Parent2")
Grandparent.__init__(self)
class Child(Parent1, Parent2):
def __init__(self):
print("Child")
Parent1.__init__(self)
Parent2.__init__(self)
c = Child() # this causes "Grandparent" to be printed twice!
Python's solution is to use super() which allows collaborative multiple inheritance. Each class must be set up to use it, including the intermediate base classes! Once you set everything up to use super, they'll call each other as necessary, and nothing will ever get called more than once.
class Grandparent:
def __init__(self):
print("Grandparent")
class Parent1(Grandparent):
def __init__(self):
print("Parent1")
super().__init__()
class Parent2(Grandparent):
def __init__(self):
print("Parent2")
super().__init__()
class Child(Parent1, Parent2):
def __init__(self):
print("Child")
super().__init__()
c = Child() # no extra "Grandparent" printout this time
In fact, every instance of multiple-inheritance in Python is a diamond shape, with the Grandparent class of object (if not something else). The object class has an __init__ method that takes no arguments (other than self) and does nothing.
It's important to note that the super().__init__ calls from the ParentX classes don't always go directly to Grandparent. When initializing an instance of Parent1 or Parent2, they will, but when instantiating an instance of Child, then Parent1.__init__'s call will go to Parent2.__init__, and only from there will Parent2's super call go to Grandparent.__init__.
Where a super call resolves to depends on the MRO (Method Resolution Order) of the instance the methods are being called on. A super call generally means "find this method in the next class in the MRO" relative to where it's called from. You can see the MRO of a class by calling SomeClass.mro(). For Child in my examples, it's: [Child, Parent1, Parent2, Grandparent, object]

inheriting python method with a call to super inside the method

I am developing a system, which has a series of single multilevel inheritance hierarachy. one of the methods (applicable to all the classes) has to perform the same thing for most of the classes, which is to pass a list to its parent class.
I know that if one doesn't define a method in one of the inherited classes, its parents' methods are used. But when we use the super method, we need to mention the name of the class being called.
One method I know to achieve this is to redefine the method at every class with class name as argument. Is there any elegant method where I can define it once at the topmost parent, and then override it only when necessary?
The implementation right now looks like this
class a(object):
def __init__(self):
self.myL = list()
print 'hello'
class b(a):
def __init__(self):
super(b,self).__init__()
def resolve(self, passVal):
print passVal
self.myL.append(passVal)
super(b,self).resolve(passVal+1)
class c(b):
def __init__(self):
super(c,self).__init__()
def resolve(self, passVal):
print passVal
self.myL.append(passVal)
super(c,self).resolve(passVal+1)
Instead if I can define resolve in class a, and then all other classes inherit the method from it. I understand a will never be able to use it. but redefining the method seems a lot unnecessary extra work.

Init child with Parent instance

I have a function which return instances of the class Parent:
def generateParent():
do_stuff
return Parent(some_parameters)
Now I want to init a subclass of Parent with the results of a call to generateParent():
class Child(Parent):
def __new__():
return generateParent(some_other_parameters)
The problem is, when I override some methods from Parent in Child and then call them in instances of Child in my program, the original Parent method gets called instead of the new one from Child. Am I doing something wrong here? Am I using the correct design here for my task?
EDIT: I don't have access neither to Parent nor generateParent()
Solution(thanks to #Paul McGuire's answer):
class Child(object):
def __init__(self):
self.obj = generateParent()
def __getattr__(self, attr):
return getattr(self.obj, attr)
Since generateParent is not your code, then instead of inheritance, you might want to use containment and delegation. That is, instead of defining a subclass, define a wrapper class that contains the generated object, forwards method calls to it when needed, but can add new behavior or modified behavior in the wrapper.
In this question, the OP had a similar situation, having a class generated in a libary, but wanting to extend the class and/or modify some behavior of the class. Look at how I added a wrapper class in that question, and you might consider doing something similar here.
Here's one way to do it:
def generateChild(params):
p = generateParent(params)
p.__class__ = Child
return p
class Child(Parent):
# put method overrides etc here
childinstance = generateChild(some_params)
Perhaps you want generateParent to be able to make instances of other classes:
def generateParent(cls=Parent):
do_stuff
return cls(some_parameters)
Now this will make a Child object:
child = generateParent(Child)
Or perhaps you want Parent and all of its derived classes to use common initialization code?
class Parent(object):
def __init__(self):
do_stuff
# init from some_parameters
class Child(Parent):
# blah..
Make your Child object able to copy information from a created Parent object:
class Child(Parent):
def __init__(self):
model_parent = generateParent()
self.a = model_parent.a
self.b = model_parent.b
# etc.

Calling private parent class method from parent class (django)

I want to call a redefined private method from an abstract parent class. I am using django if that matters.
class Parent(models.Model):
def method1(self):
#do somthing
self.__method2()
def method2(self):
pass # I also tried calling up a prent method with super
class child(Parent):
def method1(self)
super(Child, self).method1()
def __method2(self):
#do something
I get a
AttributeError: "'Chil' object has no attribute '_Parent__method2'"
What I am doing wrong ?
Initial double underscores prevent polymorphism since both the method definition and the method call get mangled, to two different names. Replace with a single underscore to fix this.
Also, double underscores are not used for "private" attributes, and you should discard whatever reference told you that they are. They're used for MI disambiguation.

Categories