Imagine a class MyMixInClass that is being used in a multiple inheritance hierarchy. when using super() to call some method, is there some way to inspect or drill in to extract the class that this method came from?
example:
class MyMixInClass:
def __init__(self):
initfunc = getattr(super(), '__init__')
# can we figure out which class the __init__ came from?
For each class in the mro sequence, you can check if there is an __init__ method in the class __dict__:
class A:
def __init__(self):
pass
class B(A):
def __init__(self):
super().__init__()
class C(A):
pass
class D(B, C):
pass
if __name__ == '__main__':
for cls in D.__mro__:
if '__init__' in cls.__dict__:
print(f'{cls.__name__} has its own init method', end='\n')
else:
print(f'{cls.__name__} has no init method', end='\n')
output:
D has no init method
B has its own init method
C has no init method
A has its own init method
object has its own init method
In this output, the first class having an __init__ method (here B), is the one called by super().__init__() in D()
Related
I have a very naïve question.
I have the following code in Python:
class A:
def __init__(self):
print("constructor A")
class B(A):
pass
ob = B()
It gives the following output:
constructor A
Similarly in the following code:
class A:
def __init__(self,name):
self.name = name
print(f'{self.name}')
class B(A):
def __init__(self):
print('Class B constructor')
ob1 = B()
ob2 = B('abc')
Shouldn't the output be:
class B constructor
abc
In fact it gives the following error:
TypeError: B.__init__() takes 1 positional argument but 2 were given
Isn't def __init__(self,name) of class A gets inherited in class B and can't we call it using ob2 = B('abc')?
Just to mention, __init__ is not constructor, it's initializer. It initializes the newly created object returned from __new__.
No, it Python attributes(including methods) of the classes are resolved using mro(Method Resolution Order), Whenever Python finds that, it stops going further.
In this example, Python didn't find fn in C, so it checks B, then A, and now it finds it. (If it didn't find it in A, it checks object class which is the last class in every class's MRO. In that case it raises AttributeError because object doesn't have fn either)
class A:
#staticmethod
def fn():
print("fn in A")
class B(A):
pass
class C(B):
pass
print(C.mro())
C.fn()
output:
[<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>]
fn in A
If for example B has defined fn, it stops there:
class A:
#staticmethod
def fn():
print("fn in A")
class B(A):
#staticmethod
def fn():
print("fn in B")
class C(B):
pass
print(C.mro())
C.fn()
output:
[<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>]
fn in B
Same thing happens to __init__.
If you want your code to work, you need to call the A's initializer inside the B's initializer. Note that the signature of the B's __init__ should compatible with the A's __init__. By compatible I mean it should take at least the number of parameters that A's __init__ takes, because you're going to pass those parameters to A.__init__:
class A:
def __init__(self, name):
print("Class A initializer")
self.name = name
print(f"{self.name}")
class B(A):
def __init__(self, name):
super().__init__(name)
print("Class B initializer")
ob2 = B("abc")
If you want the __init__() in A to be called, you have to do it yourself:
class A:
def __init__(self, name):
self.name = name
print(f'{self.name}')
class B(A):
def __init__(self, name):
print('Class B constructor')
super().__init__(name):
ob2 = B('abc')
Output
class B constructor
abc
You are trying to override constructor inside class B. class B inheritances from class A. And class A constructor except name argument. You don't need add init method inside B class here. One of the advantage using OOP is to reduces repetition. You decided to which parameters parent class will have. If you don't use parent class attributes in child class then there is no logic to create parent class. In short, parent class store attributes and methods in common, and if you want, you can add more attributes and method to child classes.
Code snippet will be like this
class A:
def __init__(self,name):
self.name = name
print(f'{self.name}')
class B(A):
def __init__(self,name):
super().__init__(name, ...) # here you can add attributes that is specific to B class
print('Class B constructor')
ob2 = B('abc')
class A:
def __init__(self, name):
self.name = name
print(f'{self.name}')
class B(A):
def __init__(self, *args):
if len(args) == 1:
super(B, self).__init__(name=args[0])
else:
print('Class B constructor')
ob1 = B()
ob2 = B('abc')
*args can control how many objects you enter, in that situation we can enter 1 or 0 objects.
if you enter 0 objects in your constructor, it will generate ('Class B constructor'), but if you enter 1 object, with super() method you call the A class constructor and name is equals to object, that is entered in the constructor.
it's because you __init__ take only one argument self which is always passed you need to add name to the definition of B
class A:
def __init__(self,name):
self.name = name
print(f'{self.name}')
class B(A):
def __init__(self, name):
print('Class B constructor')
ob2 = B('abc')
Consider the following code:
class A(object):
def __init__(self):
pass
class B(object):
def __init__(self):
self.something = 'blue'
def get_something(self):
return self.something
class C(A,B):
def __init__(self):
super().__init__()
print(self.get_something())
and then do:
c = C()
which results in something like this:
AttributeError: 'C' object has no attribute 'something'
I suppose this happens due to the constructor of B not being called when using super(). Is there a way to achieve the correct behavior with Python 3?
Superclasses should use super if their subclasses do. If you add the super().__init__() line into A and B your example should work again.
Check the method resolution order of C:
>>> C.mro()
[__main__.C, __main__.A, __main__.B, builtins.object]
This article should clear things up.
As others have mentioned, the method resolution order is key here. If you want to call multiple superclass constructors, then you will have to call them directly.
class A(object):
def __init__(self):
pass
class B(object):
def __init__(self):
self.something = 'blue'
def get_something(self):
return self.something
class C(A,B):
def __init__(self):
A.__init__(self)
B.__init__(self)
print(self.get_something())
I am working through the O Reilly Python Cookbook and have been struggling with the below code. It is to with calling a method on a parent class using super():
class Proxy:
def __init__(self, obj):
self._obj = obj
# Delegate attribute lookup to internal obj
def __getattr__(self, name):
return getattr(self._obj, name)
# Delegate attribute assignment
def __setattr__(self, name, value):
if name.startswith('_'):
super().__setattr__(name, value) # Call original __setattr__
else:
setattr(self._obj, name, value)
if __name__ == '__main__':
class A:
def __init__(self, x):
self.x = x
def spam(self):
print('A.spam')
a = A(42)
p = Proxy(a)
print(p.x)
print(p.spam())
p.x = 37
print('Should be 37:', p.x)
print('Should be 37:', a.x)
The book states:
In this code the implementation of __setatrr__() includes a name
check. If the name starts with an underscore it invokes the original
implementation of __setattr__() using super(). Otherwise, it delegates
to the internally held object self._obj.
I am confused. How does super() work then if there is no explicit base class listed?
What exactly then is super() referring to?
There is always a base class; with none explicitly mentioned, Proxy inherits directly from object.
Each class defines a method-resolution order, determined recursively by its base class(es) and its ancestors. When super() gets called, it resolves to a "proxy" of the next class in the MRO of self, whether or not that class appears in the MRO of the class you are currently defining.
Consider the following classes:
class A:
def foo(self):
print("A.foo")
class B(A):
def foo(self):
super().foo()
print("B.foo")
class C(A):
def foo(self):
super().foo()
print("C.foo")
class D(C):
def foo(self):
super().foo()
print("D.foo")
class E(B,D):
def foo(self):
super().foo()
print("E.foo")
e = E()
The MRO of E is [E, B, D, C, A, object]. When you call e.foo(), you start a chain of calls in MRO order. In particular, the call to super in B.foo does not invoke A.foo, but D.foo, a method in a class B knows nothing about, as D is not an ancestor of B. But both B and D are ancestors of E, which is what matters.
I have a testmethod in the GParent class which is inherited by Parent and Child..
How can I do that?
I tried this but its not working...
GParent.testmethod(self)
class GParent():
def testmethod(self):
print "This is test method"
class Parent():
def testmethod(self):
print "This is test method"
class Child(Parent):
def __init__(self):
print "This is init method"
GParent.testmethod(self)
c = Child()
first of all: https://docs.python.org/2/tutorial/classes.html#inheritance
At any rate...
GParent.testmethod(self) <-- calling a method before it is defined
class GParent(): <-- always inherit object on your base class to ensure you are using new style classes
def testmethod(self):
print "This is test method"
class Parent(): <-- not inheriting anything
def testmethod(self): <-- if you were inheriting GParent you would be overriding the method that is defined in GParent here.
print "This is test method"
class Child(Parent):
def __init__(self):
print "This is init method"
GParent.testmethod(self) <-- if you want to call the method you are inheriting you would use self.testmethod()
c = Child()
Take a look at this code and run it, maybe it will help you out.
from __future__ import print_function #so we can use python 3 print function
class GParent(object):
def gparent_testmethod(self):
print("Grandparent test method ")
class Parent(GParent):
def parent_testmethod(self): #
print("Parent test method")
class Child(Parent):
def child_testmethod(self):
print("This is the child test method")
c = Child()
c.gparent_testmethod()
c.parent_testmethod()
c.child_testmethod()
You cannot call GParent's testmethod without an instance of GParent as its first argument.
Inheritance
class GParent(object):
def testmethod(self):
print "I'm a grandpa"
class Parent(GParent):
# implicitly inherit __init__()
# inherit and override testmethod()
def testmethod(self):
print "I'm a papa"
class Child(Parent):
def __init__(self):
super(Child, self).__init__()
# You can only call testmethod with an instance of Child
# though technically it is calling the parent's up the chain
self.testmethod()
# inherit parent's testmethod implicitly
c = Child() # print "I'm a papa"
However, two ways of calling a parent's method explicitly is through composition or class method
Composition
class Parent(object):
def testmethod(self):
print "I'm a papa"
class Child(object):
def __init__(self):
self.parent = Parent()
# call own's testmethod
self.testmethod()
# call parent's method
self.parentmethod()
def parentmethod(self):
self.parent.testmethod()
def testmethod(self):
print "I'm a son"
c = Child()
Class method
class Parent(object):
#classmethod
def testmethod(cls):
print "I'm a papa"
class Child(object):
def __init__(self):
# call own's testmethod
self.testmethod()
# call parent's method
Parent.testmethod()
def testmethod(self):
print "I'm a son"
c = Child()
It has become advisory to use composition when dealing with multiple inheritance, since inheritance creates dependency to the parent class.
Consider the following code:
class A(object):
def __init__(self):
pass
class B(object):
def __init__(self):
self.something = 'blue'
def get_something(self):
return self.something
class C(A,B):
def __init__(self):
super().__init__()
print(self.get_something())
and then do:
c = C()
which results in something like this:
AttributeError: 'C' object has no attribute 'something'
I suppose this happens due to the constructor of B not being called when using super(). Is there a way to achieve the correct behavior with Python 3?
Superclasses should use super if their subclasses do. If you add the super().__init__() line into A and B your example should work again.
Check the method resolution order of C:
>>> C.mro()
[__main__.C, __main__.A, __main__.B, builtins.object]
This article should clear things up.
As others have mentioned, the method resolution order is key here. If you want to call multiple superclass constructors, then you will have to call them directly.
class A(object):
def __init__(self):
pass
class B(object):
def __init__(self):
self.something = 'blue'
def get_something(self):
return self.something
class C(A,B):
def __init__(self):
A.__init__(self)
B.__init__(self)
print(self.get_something())