I have two classes that inherit from the same base class. In the base class I have a very expensive method that must be ran only once and whose generated attributes must be available to the child class. How do I achieve that?
In the example below, the instantiation of the B and C child classes will both run expensive_op in A. I'd like expensive_op to be called only when I call b=B(). A is never called directly.
Moreover, I want to be able to modify the attributes of the parent class from the child class as done, for example, in the modify method in B.
Anyone able to help?
class A:
def __init__(self):
self.expensive_op()
def expensive_op(self):
self.something = #something ugly and expensive
class B(A):
def __init__(self):
super().__init__()
def modify(self,mod):
self.something = self.something+mod
class C(A):
def __init__(self):
super().__init__()
b = B()
c = C()
EDIT: in response to #0x5453's comment, do you mean the modification of A below?
class A:
def __init__(self):
self.something = None
def expensive_op(self):
if self.something is not None:
self.something = #something ugly and expensive
But if I call b=B() and then c=C(), the latter won't know about self.something. The output is
b=B()
c=C()
b.expensive_op(3)
print(b.something)
>>> 3
print(c.something is None)
>>> True
Am I missing something?
class abc():
def xyz(self):
print("Class abc")
class foo(abc):
def xyz(self):
print("class foo")
x = foo()
I want to call xyz() of the parent class, something like;
x.super().xyz()
With single inheritance like this it's easiest in my opinion to call the method through the class, and pass self explicitly:
abc.xyz(x)
Using super to be more generic this would become (though I cannot think of a good use case):
super(type(x), x).xyz()
Which returns a super object that can be thought of as the parent class but with the child as self.
If you want something exactly like your syntax, just provide a super method for your class (your abc class, so everyone inheriting will have it):
def super(self):
return super(type(self), self)
and now x.super().xyz() will work. It will break though if you make a class inheriting from foo, since you will only be able to go one level up (i.e. back to foo).
There is no "through the object" way I know of to access hidden methods.
Just for kicks, here is a more robust version allowing chaining super calls using a dedicated class keeping tracks of super calls:
class Super:
def __init__(self, obj, counter=0):
self.obj = obj
self.counter = counter
def super(self):
return Super(self.obj, self.counter+1)
def __getattr__(self, att):
return getattr(super(type(self.obj).mro()[self.counter], self.obj), att)
class abc():
def xyz(self):
print("Class abc", type(self))
def super(self):
return Super(self)
class foo(abc):
def xyz(self):
print("class foo")
class buzz(foo):
def xyz(self):
print("class buzz")
buzz().super().xyz()
buzz().super().super().xyz()
results in
class foo
Class abc
I am implementing a subclass Child where an overwritten method some_method calls the parent method first. I want to use variable a from the parent method in the child method. Is there a neat way to do this without having to modify the code of Parent?
class Parent:
def __init__(self):
pass
def some_method(self):
a = 0
class Child(Parent):
def __init__(self):
super().__init__()
def some_method(self):
super().some_method()
b = a - 1 # Here I would like to keep using `a`
Regarding multiple parent inheritance, when I call the super.__init__, why doesn't parent2's __init__ function get called? Thanks.
class parent(object):
var1=1
var2=2
def __init__(self,x=1,y=2):
self.var1=x
self.var2=y
class parent2(object):
var4=11
var5=12
def __init__(self,x=3,y=4):
self.var4=x
self.var5=y
def parprint(self):
print self.var4
print self.var5
class child(parent, parent2):
var3=5
def __init__(self,x,y):
super(child, self).__init__(x,y)
childobject = child(9,10)
print childobject.var1
print childobject.var2
print childobject.var3
childobject.parprint()
Output is
9
10
5
11
12
If you want to use super in child to call parent.__init__ and parent2._init__, then both parent __init__s must also call super:
class parent(Base):
def __init__(self,x=1,y=2):
super(parent,self).__init__(x,y)
class parent2(Base):
def __init__(self,x=3,y=4):
super(parent2,self).__init__(x,y)
See "Python super method and calling alternatives" for more details on the sequence of calls to __init__ caused by using super.
class Base(object):
def __init__(self,*args):
pass
class parent(Base):
var1=1
var2=2
def __init__(self,x=1,y=2):
super(parent,self).__init__(x,y)
self.var1=x
self.var2=y
class parent2(Base):
var4=11
var5=12
def __init__(self,x=3,y=4):
super(parent2,self).__init__(x,y)
self.var4=x
self.var5=y
def parprint(self):
print self.var4
print self.var5
class child(parent, parent2):
var3=5
def __init__(self,x,y):
super(child, self).__init__(x,y)
childobject = child(9,10)
print childobject.var1
print childobject.var2
print childobject.var3
childobject.parprint()
You might be wondering, "Why use Base?". If parent and parent2 had inherited directly from object, then
super(parent2,self).__init__(x,y) would call object.__init__(x,y). That raises a TypeError since object.__init__() takes no parameters.
To workaround this issue, you can make a class Base which accepts arguments to __init__ but does not pass them on to object.__init__. With parent and parent2 inheriting from Base, you avoid the TypeError.
Because parent is next in method resolution order (MRO), and it never uses super() to call into parent2.
See this example:
class Base(object):
def __init__(self, c):
print('Base called by {0}'.format(c))
super().__init__()
class ParentA(Base):
def __init__(self, c):
print('ParentA called by {0}'.format(c))
super().__init__('ParentA')
class ParentB(Base):
def __init__(self, c):
print('ParentB called by {0}'.format(c))
super().__init__('ParentB')
class Child(ParentA, ParentB):
def __init__(self, c):
print('Child called by {0}'.format(c))
super().__init__('Child')
Child('Construct')
print(Child.mro())
This will output:
Child called by Construct
ParentA called by Child
ParentB called by ParentA
Base called by ParentB
[<class '__main__.Child'>, <class '__main__.ParentA'>, <class '__main__.ParentB'>, <class '__main__.Base'>, <class 'object'>]
Python multiple inheritance is like a chain, in Child class mro, the super class of ParentA is ParentB, so you need call super().__init__() in ParentA to init ParentB.
If you change super().__init__('ParentA') to Base.__init__(self, 'ParentA'), this will break the inheritance chain, output:
Child called by Construct
ParentA called by Child
Base called by ParentA
[<class '__main__.Child'>, <class '__main__.ParentA'>, <class '__main__.ParentB'>, <class '__main__.Base'>, <class 'object'>]
More info about MRO
I'm writing a class in python that has multiple subclasses in it I have:
class Parent:
def __init__(self, parameters):
self.MethodA(parameters)
def MethodA(parameters):
doStuff
class child1(Parent):
def MethodA(parameters):
doOtherStuff
Which method will be used when I make an object of type child1?
Try it and see:
class Parent(object):
def __init__(self, params):
self.method(params)
def method(self, params):
print "Parent's method called with", params
class Child(Parent):
def method(self, params):
print "Child's method called with", params
Child('foo')
outputs:
Child's method called with foo
child1.MethodA() would be called. Methods in most dynamic languages are essentially always virtual since the lookup of self is done at runtime.
It can be usefull for you - method resolution order.
All methods in Python are effectively virtual.
>>> class Parent(object):
def __init__(self):
self.MethodA()
def MethodA(self):
print 'A method'
>>> class child1(Parent):
def MethodA(self):
print 'child1 method'
>>> x = child1()
child1 method
>>> x.MethodA()
child1 method