Call a parent class method from a child class in Python 2 - python

I want to call parent class method using super() in Python 2.
In Python 3, I'd code it like this:
class base:
#classmethod
def func(cls):
print("in base: " + cls.__name__)
class child(base):
#classmethod
def func(cls):
super().func()
print("in child: " + cls.__name__)
child.func()
with this output:
in base: child
in child: child
However, I have no idea, how do this in Python 2. Of course, I can use base.func(), but I don't like to specify parent class name in addition and mainly I get unwanted result:
in base: base
in child: child
With cls (cls is child) as first argument in super() function call, I get this error:
TypeError: must be type, not classobj
Any idea how do it using super() or analogous function in which I don't have to specify name of parent class?

furthering the other answer you can do classmethods for it like
class base(object):
#classmethod
def func(cls):
print("in base: " + cls.__name__)
class child(base):
#classmethod
def func(cls):
super(cls, cls).func()
print("in child: " + cls.__name__)
child.func()

You parent object needs to inherit from object in python 2. So:
class base(object):
def func(self):
print("in base")
class child(base):
def func(self):
super(child, self).func()
print("in child")
c = child()
c.func()

I was trying to do something similar where I was trying to basically "walk up" the inheritance chain until I found a certain base class and then do something there with the class name. The issue I was having was that ALL of these answers assume that you know the name of the class you are trying to get the super of. I tried the "super(cls, cls)" approach but got the "inifinite recursion" issue described above. Here is where I landed
#classmethod
def parent_name(cls):
if BaseDocument in cls.__bases__:
# This will return the name of the first parent that subclasses BaseDocument
return cls.__name__
else:
for klass in cls.__bases__:
try:
parent_name = klass.parent_name()
if parent_name is not None:
return parent_name
except AttributeError:
pass
return None

Related

Overwrite class method from parent without affecting downstream classes

I would like to overwrite an inherited method in a class (see below example for __init__ method) while letting its children still use the Parents version.
I know that I could achieve the desired behaviour redefining the __init__ method in the GrandChild class or using multiple inheritance. However my question aims at a way to achieve the same with changes only to the Child class and its __init__ implementation.
(The actual use case is significantly more complex 'legacy code' with several classes on each level. The motivation of this question is therefore to achieve the desired behaviour in the respective class without having to touch the implementation of the other classes or the inheritance structure)
If this is impossible I would also appreciate an explanation to that effect.
class Parent:
def __init__(self, a,b):
self.a = a
self.b = b
def __str__(self):
return f"{self.a}, {self.b}"
class Child(Parent):
# I would like to overwrite this method only for the Child Class and none of its children / downstream inhertiances
def __init__(self, a):
super().__init__(a, None)
class GrandChild(Child):
# This Class should use the __init__ method of class Parent
pass
parent = Parent("a","b")
child = Child("c")
# This throws a Type error right now since it calls the init method of class Child
grandchild = GrandChild("d", "e")
EDIT:
As mentioned above I am aware that I can achieve the desired behaviour in different ways such as changing the class structure (as below). However the question is really more about wether python allows doing it with changes only to the Child class. If this is actually impossible (not merely undesirable) in python, an explanation why would do more to answer my question than providing alternative implementations that change anything beyond the implementation of the Child class.
class ChildCommonFunctionality(Parent):
# Use this class for all common functionality originally provided by Child Class
pass
class Child(ChildCommonFunctionality):
# Use this class to override the init method
def __init__(self, a):
super().__init__(a, None)
class GrandChild(ChildCommonFunctionality):
# This Class should use the __init__ method of class Parent
pass
I have found a way using _init_subclass to make sure that all subclasses of Child use the constructor of Parent instead of the one defined in Child inspired by this post:
class Parent:
def __init__(self, a,b):
self.a = a
self.b = b
def __str__(self):
return f"{self.a}, {self.b}"
class Child(Parent):
# I would like to overwrite this method only for the Child Class and none of its children / downstream inhertiances
def __init__(self, a):
super().__init__(a, None)
def __init_subclass__(cls):
cls.__init__ = super().__init__
class GrandChild(Child):
# This Class should use the __init__ method of class Parent
pass
Even though this is a bit hacky it provides the desired functionality of actually bypassing Childs init method
You could do :
class Parent:
def __init__(self, a, b = None):
self.a = a
self.b = b
def __str__(self):
return f"{self.a}, {self.b}"
class Child(Parent):
# I would like to overwrite this method only for the Child Class and none of its children / downstream inhertiances
def __init__(self, a, b = None):
super().__init__(a, b) # Or None instead of b... but that's not good when called by GrandChild
class GrandChild(Child):
# This Class should use the __init__ method of class Parent
pass
parent = Parent("a","b")
child = Child("c")
grandchild = GrandChild("d", "e")
EDIT : you could also replace the optional parameter by a mandatory one in GrandChild :
class GrandChild(Child):
def __init__(self, a, b):
super().__init__(a, b)
This code might do the trick, adding a few lines to the suggestion of #dspr:
class Parent:
def __init__(self, a, b = None):
self.a = a
self.b = b
def __str__(self):
return f"{self.a}, {self.b}"
class Child(Parent):
# I would like to overwrite this method only for the Child Class and none of its children / downstream inhertiances
def __init__(self, a, b = None):
if type(self) == Child:
if b is not None:
raise ValueError(
"Second argument is not allowed for direct use in Child class")
super().__init__(a, None) #Or (a, b) if you trust b to be None as it is here
else:
super().__init__(a, b)
class GrandChild(Child):
# This Class should use the __init__ method of class Parent
pass
parent = Parent("a","b")
child = Child("c")
print(child.b) # None
grandchild = GrandChild("d", "e")
print(grandchild.b) # e
child = Child("f", "g")
print(child.b) # ValueError

Calling super() without a base class

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.

Using decorator specified in Child class on Parent class methods

Let's say I have two classes Child and Parent (which is base class for Child). I have another class Dec which contains a decorator dec I'd like to be used over Parent methods. I want to be able to specify a Dec object that should be used in Child.
Here's what I've tried so far:
class Dec():
def dec(self, func):
def wrapper(self):
print("Before func call")
func(self)
print("After func call")
return wrapper
class Parent():
dec = None
#dec.dec
def p(self):
print('hello')
dec = Dec()
class Child(Parent):
a = dec
t = Child()
t.p()
So, I got
AttributeError: 'NoneType' object has no attribute 'dec'
at #dec.dec.
Is there any option to specify a class with decorator that should be used in Child class?
The issue you are experiencing here has to do with scoping.
When I ran this code, I received this error:
...<stack trace>...
File ".\__main__.py", line 10, in <module>
class Parent():
File ".\__main__.py", line 13, in Parent
#dec.dec
AttributeError: 'NoneType' object has no attribute 'dec'
Using that, you can see that you have some scoping issues. In this file, you define dec several times. Rather than instantiate Dec like you do on line 11, define Dec.dec(...) as a #classmethod, callable from the class itself, instead of just an instance of the class.
here is a potential solution:
class Dec():
#classmethod
def dec(self, func):
def wrapper(self):
print("Before func call")
func(self)
print("After func call")
return wrapper
class Parent():
#Dec.dec
def p(self):
print('hello')
class Child(Parent):
pass # you didn't really need anything here.
t = Child()
t.p()
This provides what I believe to be the expected behavior:
Before func call
hello
After func call
As an alternative to the other solution, here's another approach:
define some sort of generic structure for your process, and then change the "decorator" or "extended processor" for your classes...
class Parent:
extended_processor = None
def p(self):
if (self.extended_processor is not None and
hasattr(self.extended_processor, "before") and
callable(self.extended_processor.before)):
self.extended_processor.before()
print('parent says hello')
if (self.extended_processor is not None and
hasattr(self.extended_processor, "after") and
callable(self.extended_processor.after)):
self.extended_processor.after()
class ex_proc:
#classmethod
def before(cls):
print("ex_proc before")
#classmethod
def after(cls):
print("ex_proc after")
class Child(Parent):
extended_processor = ex_proc
print("\n=== PARENT ===")
par = Parent()
par.p()
print("\n=== CHILD ===")
chi = Child()
chi.p()
this provides the following output:
=== PARENT ===
parent says hello
=== CHILD ===
ex_proc before
parent says hello
ex_proc after

Accessing a method of a parent class in the sublass

i am new to OOP.
i have a parent class with a method that i wanna access in my subclass. But i cannot figure out the right syntax for that. I cannot find a clear cut example anywhere
Members in the base class are simply available to the subclass as well (unless they are overwritten):
class Base:
def example (self):
print('This is in the base class')
class Subclass (Base):
def test (self):
self.example()
An object of type Subclass can now access example directly or indirectly:
>>> x = Subclass()
>>> x.test()
This is in the base class
>>> x.example()
This is in the base class
class Parent(object):
def __init__(self, name):
self.name = name
def output(self):
print self.name
class Child(Parent):
def __init__(self, name, age):
Parent.__init__(self, name)
self.age = age
def output(self):
super(Child, self).output()
print self.age
if __name__ == '__main__':
a = Parent("wy")
b = Child("zhang", 10)
a.output()
b.output()
You can try this code.

Pass a parent class as an argument?

Is it possible to leave a parent class unspecified until an instance is created?
e.g. something like this:
class SomeParentClass:
# something
class Child(unspecifiedParentClass):
# something
instance = Child(SomeParentClass)
This obviously does not work. But is it possible to do this somehow?
You can change the class of an instance in the class' __init__() method:
class Child(object):
def __init__(self, baseclass):
self.__class__ = type(self.__class__.__name__,
(baseclass, object),
dict(self.__class__.__dict__))
super(self.__class__, self).__init__()
print 'initializing Child instance'
# continue with Child class' initialization...
class SomeParentClass(object):
def __init__(self):
print 'initializing SomeParentClass instance'
def hello(self):
print 'in SomeParentClass.hello()'
c = Child(SomeParentClass)
c.hello()
Output:
initializing SomeParentClass instance
initializing Child instance
in SomeParentClass.hello()
Have you tried something like this?
class SomeParentClass(object):
# ...
pass
def Child(parent):
class Child(parent):
# ...
pass
return Child()
instance = Child(SomeParentClass)
In Python 2.x, also be sure to include object as the parent class's superclass, to use new-style classes.
You can dynamically change base classes at runtime. Such as:
class SomeParentClass:
# something
class Child():
# something
def change_base_clase(base_class):
return type('Child', (base_class, object), dict(Child.__dict__))()
instance = change_base_clase(SomeParentClass)
For example:
class Base_1:
def hello(self):
print('hello_1')
class Base_2:
def hello(self):
print('hello_2')
class Child:pass
def add_base(base):
return type('Child', (base, object), dict(Child.__dict__))()
# if you want change the Child class, just:
def change_base(base):
global Child
Child = type('Child', (base, object), dict(Child.__dict__))
def main():
c1 = add_base(Base_1)
c2 = add_base(Base_2)
c1.hello()
c2.hello()
main()
Result:
hello_1
hello_2
Works well in both python 2 and 3.
For more information, see the related question How to dynamically change base class of instances at runtime?

Categories