Suppose I have a parent class Parent and two child classes Child1 and Child2. Suppose Parent contains a method usable by both subclasses. Is it possible for this method to return a new instance of the same class of whatever class calls it, even if the particular subclass calling it is not known when the method is defined in the parent class?
class Parent:
def __init__(self, foo):
pass
def my_method(self):
return self.__class__(foo)
class Child1(Parent):
def __init__(self, foo):
super().__init__(foo)
class Child2(Parent):
def __init__(self, foo):
super().__init__(foo)
parent = Parent('hello')
type(p.my_method())
>>> class<Parent>
child1 = Child1('hey')
type(child1.my_method())
>>> class<Child1>
child2 = Child2('yo')
type(child2.my_method())
>>> class<Child2>
EDIT
I had made a mistake in passing foo without initializing it as an attribute first. The following code achieves the behavior I'm going for, but I'd be interested to know if this could lead to design problems down the road, so feedback on that would be appreciated.
class Parent:
def __init__(self, foo):
self.foo = foo
def my_method(self):
return self.__class__(self.foo)
class Child1(Parent):
def __init__(self, foo):
super().__init__(foo)
class Child2(Parent):
def __init__(self, foo):
super().__init__(foo)
p = Parent(foo='hi')
print(type(p.my_method()))
>>>__main__.Parent
c1 = Child1(foo='hey')
print(type(c1.my_method()))
>>>__main__.Child1
c2 = Child2(foo='hey')
print(type(c2.my_method()))
>>>__main__.Child2
What you have won't work, because you have my_method being an instance method, but you're trying to call it without an instance.
If you make that a class method, it can work, although I personally think this is a bad design choice:
class Parent:
def __init__(self, foo):
pass
#classmethod
def my_method(cls):
return cls(0)
class Child1(Parent):
def __init__(self, foo):
super().__init__(foo)
class Child2(Parent):
def __init__(self, foo):
super().__init__(foo)
print(type(Parent.my_method()))
print(type(Child1.my_method()))
print(type(Child2.my_method()))
Output:
<class '__main__.Parent'>
<class '__main__.Child1'>
<class '__main__.Child2'>
I have overriden a method in a child class.
This method is used in the parent class constructor.
When calling super().__init__ in the child class the child's method is executed instead of the parent's one inside the parent's constructor.
Example:
class A:
def __init__(self):
self.method()
def method(self):
print('parent method!')
class B(A):
def __init__(self):
super().__init__()
def method(self):
print('child method!')
b = B()
Output: child method!
While I want to get parent method!
Edit:
I need the parent's class constructor to use the not-overriden method, but after that each call to method() from the parent class should call overriden one.
Because I need the parent's contructor to be called with the no-overriden method but then I need to use the overriden one each time.
This indicates you are asking one method to do too many things. Split method into two parts: one that is not overridden and called by A.__init__, and another that can be overriden. Something like
class A:
def __init__(self):
self._init_method()
def _init_method(self):
print('parent method!')
def method(self):
self._init_method()
class B(A):
def __init__(self):
super().__init__()
def method(self):
print('child method!')
b = B()
_init_method does what you claim needs to be done from A.__init__. At the same time, you can have method do the same thing, unless you override it, in which case you'll do whatever it is you want B.method to do.
If you have that kind of situation, I would suggest something like:
class B(A):
def __init__(self):
super().__init()
self.n_method = 1
def method(self):
if self.n_method == 1:
super().method()
else:
do method B stuffs
self.n_method += 1
In A's __init__() method, you could write A.method(self) rather than self.method() to be more explicit about which method to call.
Usually when I see a subclass, it calls the superclass in the constructor like below:
class Boss(object):
def __init__(self, name, attitude, behaviour, face):
self.name = name
self.attitude = attitude
self.behaviour = behaviour
self.face = face
def get_attitude(self):
return self.attitude
def get_behaviour(self):
return self.behaviour
def get_face(self):
return self.face
class GoodBoss(Boss):
def __init__(self, name, attitude, behaviour, face):
super().__init__(name, attitude, behaviour, face)
However on a website I saw an example of a subclass:
class Parent(object):
def __init__(self):
self.value = 5
def get_value(self):
return self.value
class Child(Parent):
pass
In the last example why doesn't the Child subclass include super().__init__()?
Try to understand this simple code:
class Parent(object):
def __init__(self):
print('Parent is called!')
class ChildA(Parent):
pass
class ChildB(Parent):
def __init__(self):
print('ChildB is called!')
class ChildC(Parent):
def __init__(self):
print('ChildC is called!')
super().__init__()
p = Parent()
print()
ca = ChildA()
print()
cb = ChildB()
print()
cc = ChildC()
print()
print(ChildA.mro())
print(ChildB.mro())
print(ChildC.mro())
Output:
Parent is called!
Parent is called!
ChildB is called!
ChildC is called!
Parent is called!
[<class '__main__.ChildA'>, <class '__main__.Parent'>, <class 'object'>]
[<class '__main__.ChildB'>, <class '__main__.Parent'>, <class 'object'>]
[<class '__main__.ChildC'>, <class '__main__.Parent'>, <class 'object'>]
Key Observations:
If a class does not have an implementation of the __init__() method, then its parent's __init__() will be called if it exists.
If a class has an implementation of the __init__() method, then it will be called. However its parent's __init__() will not be called automatically.
If a class has an implementation of the __init__() method, then it will be called. We can call its parent's __init__() method by explicitly calling it with super().method(*args, **kwargs)
Classes don't strictly require __init__() methods at all. You can have a class consisted with only class/static attributes/methods and it'll be just as valid, though its usefulness remains to be debated.
The reason you see most classes have __init__() method is because most of the time when a class is instantiated you'd want to initialize the class with some values/handling. If your Child class is not doing anything different than the Parent's __init__() method, there's no need to redefine __init__().
If the Child class needs to redefine __init__ but still wants to rely on the Parent's __init__ method, then that's when you need super().__init__().
In your first example, Subclass is calling the init method of your parent class in the constructor method. This approach is basically used when we need to set some attributes of subclass method but after/before calling the superclass method. Let's consider below example with setting the age attribute in subclass:
class Boss(object):
def __init__(self, name, attitude, behaviour, face):
self.name = name
self.attitude = attitude
self.behaviour = behaviour
self.face = face
def get_attitude(self):
return self.attitude
def get_behaviour(self):
return self.behaviour
def get_face(self):
return self.face
class GoodBoss(Boss):
def __init__(self,
name,
attitude,
behaviour,
face, age):
super().__init__(name, attitude, behaviour, face)
self.age = age # here age is an attribute of only GoodBoss class, Not Superclass
Here your subclass is setting the age attribute which isn't an attribute of your parent class.This pattern is used when your subclass is doing something different from superclass in constructor function.
In second example, we are not setting/calling any method in constructor function of child class. When we make the object of child class, the parent class init function would be called.Your subclass is not doing any different.
These are completely depend on the code design.
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
class A(object):
def __init__(self, a, b, c):
#super(A, self).__init__()
super(self.__class__, self).__init__()
class B(A):
def __init__(self, b, c):
print super(B, self)
print super(self.__class__, self)
#super(B, self).__init__(1, b, c)
super(self.__class__, self).__init__(1, b, c)
class C(B):
def __init__(self, c):
#super(C, self).__init__(2, c)
super(self.__class__, self).__init__(2, c)
C(3)
In the above code, the commented out __init__ calls appear to the be the commonly accepted "smart" way to do super class initialization. However in the event that the class hierarchy is likely to change, I have been using the uncommented form, until recently.
It appears that in the call to the super constructor for B in the above hierarchy, that B.__init__ is called again, self.__class__ is actually C, not B as I had always assumed.
Is there some way in Python-2.x that I can maintain proper MRO (with respect to initializing all parent classes in the correct order) when calling super constructors while not naming the current class (the B in in super(B, self).__init__(1, b, c))?
Short answer: no, there's no way to implicitly invoke the right __init__ with the right arguments of the right parent class in Python 2.x.
Incidentally, the code as shown here is incorrect: if you use super().__init__, then all classes in your hierarchy must have the same signature in their __init__ methods. Otherwise your code can stop working if you introduce a new subclass that uses multiple inheritance.
See http://fuhm.net/super-harmful/ for a longer description of the issue (with pictures).
Your code has nothing to do with method resolution order. Method resolution comes in the case of multiple inheritance which is not the case of your example. Your code is simply wrong because you assume that self.__class__ is actually the same class of the one where the method is defined and this is wrong:
>>> class A(object):
... def __init__(self):
... print self.__class__
...
>>>
>>> class B(A):
... def __init__(self):
... A.__init__(self)
...
>>> B()
<class '__main__.B'>
<__main__.B object at 0x1bcfed0>
>>> A()
<class '__main__.A'>
<__main__.A object at 0x1bcff90>
>>>
so when you should call:
super(B, self).__init__(1, b, c)
you are indeed calling:
# super(self.__class__, self).__init__(1, b, c)
super(C, self).__init__(1, b, c)
EDIT: trying to better answer the question.
class A(object):
def __init__(self, a):
for cls in self.__class__.mro():
if cls is not object:
cls._init(self, a)
def _init(self, a):
print 'A._init'
self.a = a
class B(A):
def _init(self, a):
print 'B._init'
class C(A):
def _init(self, a):
print 'C._init'
class D(B, C):
def _init(self, a):
print 'D._init'
d = D(3)
print d.a
prints:
D._init
B._init
C._init
A._init
3
(A modified version of template pattern).
Now parents' methods are really called implicitly, but i have to agree with python zen where explicit is better than implicit because the code is lesser readable and the gain is poor. But beware that all _init methods have the same parameters, you cannot completely forget about parents and I don't suggest to do so.
For single inheritance, a better approach is explicitly calling parent's method, without invoking super. Doing so you don't have to name the current class, but still you must care about who is the parent's class.
Good reads are: how-does-pythons-super-do-the-right-thing and the links suggested in that question and in particularity Python's Super is nifty, but you can't use it
If hierarchy is likely to change is symptoms of bad design and has consequences in all the parts who are using that code and should not be encouraged.
EDIT 2
Another example comes me in mind, but which uses metaclasses. Urwid library uses metaclass to store an attribute, __super, in class so that you need just to access to that attribute.
Ex:
>>> class MetaSuper(type):
... """adding .__super"""
... def __init__(cls, name, bases, d):
... super(MetaSuper, cls).__init__(name, bases, d)
... if hasattr(cls, "_%s__super" % name):
... raise AttributeError, "Class has same name as one of its super classes"
... setattr(cls, "_%s__super" % name, super(cls))
...
>>> class A:
... __metaclass__ = MetaSuper
... def __init__(self, a):
... self.a = a
... print 'A.__init__'
...
>>> class B(A):
... def __init__(self, a):
... print 'B.__init__'
... self.__super.__init__(a)
...
>>> b = B(42)
B.__init__
A.__init__
>>> b.a
42
>>>
Perhaps what you are looking for is metaclasses?
class metawrap(type):
def __new__(mcs,name, bases, dict):
dict['bases'] = bases
return type.__new__(mcs,name,bases,dict)
class A(object):
def __init__(self):
pass
def test(self):
print "I am class A"
class B(A):
__metaclass__ = metawrap
def __init__(self):
pass
def test(self):
par = super(self.bases[0],self)
par.__thisclass__.test(self)
foo = B()
foo.test()
Prints "I am class A"
What the metaclass does is overriding the initial creation of the B class (not the object) and makes sure that the builtin dictionary for each B object now contains a bases array where you can find all the baseclasses for B
To my knowledge, the following isn't commonly done. But it does seem to work.
Methods in a given class definition always mangle double-underscore attributes to include the name of the class they're defined in. So, if you stash a reference to the class in name-mangled form where the instances can see it, you can use that in the call to super.
An example stashing the references on the object itself, by implementing __new__ on the baseclass:
def mangle(cls, name):
if not name.startswith('__'):
raise ValueError('name must start with double underscore')
return '_%s%s' % (cls.__name__, name)
class ClassStasher(object):
def __new__(cls, *args, **kwargs):
obj = object.__new__(cls)
for c in cls.mro():
setattr(obj, mangle(c, '__class'), c)
return obj
class A(ClassStasher):
def __init__(self):
print 'init in A', self.__class
super(self.__class, self).__init__()
class B(A):
def __init__(self):
print 'init in B', self.__class
super(self.__class, self).__init__()
class C(A):
def __init__(self):
print 'init in C', self.__class
super(self.__class, self).__init__()
class D(B, C):
def __init__(self):
print 'init in D', self.__class
super(self.__class, self).__init__()
d = D()
print d
And, doing a similar thing, but using a meta-class and stashing the __class references on the class objects themselves:
class ClassStasherType(type):
def __init__(cls, name, bases, attributes):
setattr(cls, mangle(cls, '__class'), cls)
class ClassStasher(object):
__metaclass__ = ClassStasherType
class A_meta(ClassStasher):
def __init__(self):
print 'init in A_meta', self.__class
super(self.__class, self).__init__()
class B_meta(A_meta):
def __init__(self):
print 'init in B_meta', self.__class
super(self.__class, self).__init__()
class C_meta(A_meta):
def __init__(self):
print 'init in C_meta', self.__class
super(self.__class, self).__init__()
class D_meta(B_meta, C_meta):
def __init__(self):
print 'init in D_meta', self.__class
super(self.__class, self).__init__()
d = D_meta()
print d
Running this all together, as one source file:
% python /tmp/junk.py
init in D <class '__main__.D'>
init in B <class '__main__.B'>
init in C <class '__main__.C'>
init in A <class '__main__.A'>
<__main__.D object at 0x1004a4a50>
init in D_meta <class '__main__.D_meta'>
init in B_meta <class '__main__.B_meta'>
init in C_meta <class '__main__.C_meta'>
init in A_meta <class '__main__.A_meta'>
<__main__.D_meta object at 0x1004a4bd0>