Let's say we have:
class Parent():
def __init__(self):
foo()
def foo(self):
//do stuff
class Child(Parent):
def __init__(self):
Parent.__init__()
class Grandchild(Child):
def __init__(self):
Child.__init__()
def foo(self):
//different stuff
There are a lot of classes at the Child level that use the same foo(). Grandchild level has a slightly different version of foo, but when Grandchild is initiated, the foo() call in Parent.__init__() uses the Parent.foo() instead of Grandchild.foo().
Is there a correct practice when in comes to this kind of situation?
You're not calling the base classes' __init__() methods properly—you need to pass along the self argument to them:
class Parent:
def __init__(self):
self.foo()
def foo(self):
print('Parent stuff')
class Child(Parent):
def __init__(self):
Parent.__init__(self)
class Grandchild(Child):
def __init__(self):
Child.__init__(self)
def foo(self):
print('Grandchild stuff')
if __name__ == '__main__':
gc = Grandchild() # -> Grandchild stuff
If you use super() instead of explicitly stating the base class, you don't have to do that:
class Parent:
def __init__(self):
self.foo()
def foo(self):
print('Parent stuff')
class Child(Parent):
def __init__(self):
# Parent.__init__(self)
super().__init__()
class Grandchild(Child):
def __init__(self):
# Child.__init__(self)
super().__init__()
def foo(self):
print('Grandchild stuff')
if __name__ == '__main__':
gc = Grandchild() # -> Grandchild stuff
Another advantage is that you likely wouldn't have to change the code in a subclass' __init__() method if you changed its base class.
Related
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'>
How to inherit all class 'A' attributes and methods, but 'b()'?
class A:
def __init__(self):
# attributes
pass
#classmethod
def b(cls):
# logic
pass
class B(A):
def __init__(self, **kwargs):
super().__init__(**kwargs)
def b(self):
# nothing
pass
do not use this old method( if there is another way to do it ):
class B(A):
def __init__(self, attributes):
super().__init__(self, attributes)
You can reimplement b() to raise an error:
class A:
def __init__(self):
# attributes
pass
#classmethod
def b(cls):
# logic
pass
class B(A):
def __init__(self, **kwargs):
super().__init__(**kwargs)
#classmethod
def b(cls):
raise TypeError("method b is not supported in class B")
Also, if b() is a classmethod, you should probably override it as a classmethod.
Put that method in a separate class and don't inherit it.
class A:
def __init__(self):
# attributes
pass
class A1:
#classmethod
def b(cls):
# logic
pass
class B(A):
def __init__(self, **kwargs):
super().__init__(**kwargs)
Use multiple inheritance when you want that method.
class C(A,A1):
pass
class Parent:
def __init__(self):
self.__num = 100
def show(self):
print("Parent:",self.__num)
class Child(Parent):
def __init__(self):
self.__var = 10
def show(self):
super().show()
print("Child:",self.__var)
obj1 = Child()
obj1.show()
File "main.py", line 12, in show
super().show()
File "main.py", line 6, in show
print("Parent:",self.__num)
AttributeError: 'Child' object has no attribute '_Parent__num'
You need to initialize the parent instance in your child class, because the __num attribute is only set during Parent's initialization, and not during the Child's.
class Child(Parent):
def __init__(self):
super().__init__()
self.__var = 10
def show(self):
super().show()
print("Child:",self.__var)
class Parent:
def __init__(self):
self.__num = 100
def show(self):
print("Parent:",self.__num)
class Child(Parent):
def __init__(self):
super().__init__() # Solution
self.__var = 10
def show(self):
super().show()
print("Child:",self.__var)
obj1 = Child()
obj1.show()
To avoid overridding try this.
class Parent:
def __init__(self):
self.__num = 100
def show(self):
print("Parent:",self.__num)
class Child(Parent):
def __init__(self):
Parent.__init__(self)
self.__var=10
def show1(self):
print("Child:",self.__var)
obj1 = Child()
obj1.show()
Change your Child.init to something like:
def __init__(self):
self.__var = 10
super().__init__()
as the other answers already said, you need to add super().__init__() to the __init__ of your child class.
but also note that there is something called name mangling at work here. read e.g. the 3. Double Leading Underscore: __var on this page.
the short version is: if you wanted to use the attribute self.__num also in the child class you should rename it to self._num (one underscore only).
I override a function, but would like to get hold of the parent's function from within the parent.
>>> class a:
... def __init__(self):
... print(self.f)
... def f(self):
... pass
...
>>> class b(a):
... def __init__(self):
... super(b, self).__init__()
... def f(self):
... pass
...
>>> b()
<bound method b.f of <__main__.b object at 0x000002E297A96160>>
I'd like the printout to say a.f.
You could use name mangling to make self.__f refer to A.__f from within A's class definition.
Name mangling is helpful for letting subclasses override methods without breaking intraclass method calls
class A:
def __init__(self):
self.__f()
def f(self):
print('A.f')
__f = f # Private copy of A's `f` method
class B(A):
def __init__(self):
super(B, self).__init__()
def f(self):
print('B.f')
b = B()
b.f()
prints
A.f
B.f
class a(object):
def __init__(self):
pass
def f(self):
print 'Parent Method...'
class b(a):
def __init__(self):
super(b, self).__init__()
a.f(self) #referance the parent class rather than the child class, because the child overrides the parent method.
self.f()
def f(self):
print "Childs Method..."
b()
I have a class scheme with 2-levels of inheritance. My expectation is that each class constructor would run through- and yet the mid-level class constructor never seems to get hit. What's missing here?
class Base(object):
def __init__(self):
print "BASE"
class Next(Base):
def __init__(self):
super(Base, self).__init__()
print "NEXT"
class Final(Next):
def __init__(self):
super(Next, self).__init__()
print "FINAL"
f = Final()
Outputs:
BASE
FINAL
Why does "NEXT" not print??
You should be calling super() with the current class, not the parent.
class Base(object):
def __init__(self):
super(Base, self).__init__()
print "BASE"
class Next(Base):
def __init__(self):
super(Next, self).__init__()
print "NEXT"
class Final(Next):
def __init__(self):
super(Final, self).__init__()
print "FINAL"
f = Final()
At first glance this might seem redundant ("why can't it just get the class from self?") - but keep in mind that the same self is passed to all three of these __init__ methods when f is created, and that self is always of class Final. Thus, you have to pass super() the class that you want it to find the parent of.
class Base(object):
def __init__(self):
print "BASE"
class Next(Base):
def __init__(self):
super(Next, self).__init__()
print "NEXT"
class Final(Next):
def __init__(self):
super(Final, self).__init__()
print "FINAL"
f = Final()