If I am inheriting from a class and not changing anything in a method, is it required to use super to initialize the method from the parent class?
class A:
def __init__(self):
self.html = requests.get("example.com").text
class B(A):
def __init__(self):
# is this needed?
super(B, self).__init__()
def new_method(self):
print self.html
Because you created a __init__ method in your class B, it overrides the method in class A. If you want it executed, you'll have to use super(), yes.
However, if you are not doing anything else in B.__init__, you may as well just omit it:
class A:
def __init__(self):
self.html = requests.get("example.com").text
class B(A):
def new_method(self):
print self.html
If you want to do anything in addition to what A.__init__() does, then it makes sense to create a B.__init__() method, and from that method, invoke the parent __init__.
It's not needed to define the overriding method at all. Python's default behavior is to call the method on the parent class (the next class in the method resolution order) if the current class doesn't override it.
>>> class Foo(object):
... def __init__(self):
... print("Foo")
...
>>> class Bar(Foo): pass
...
>>> Bar()
Foo
<__main__.Bar object at 0x7f5ac7d1b990>
Notice "Foo" got printed when I initialized a Bar instance.
If you do define the method, you need to call the super class's method (either explicitly or via super) if you want to make sure that it gets called.
Related
Is it possible to skip classes in the method resolution order when calling for methods?
For example,
super().super()
I read the docs here: https://docs.python.org/3/library/functions.html#super
that lead me to this code
class A:
def show(self):
print("A")
class B(A):
def __init__(self):
print("B")
def s(self):
return super()
class C(B):
def __init__(self):
super().s().show()
c = C()
c
See that super returns a proxy object that seems not to have the super method (because i tried and the interpreter told me it doesn't). But you do have the others methods from the class, so this way I could get a proxy from its grandparent to use its methods
super() in the class definition for FooClass is shorthand for super(FooClass, self). Using this, we can do this:
class Grandparent:
def test(self):
print('grandparent gets called')
class Parent(Grandparent):
def test(self):
super().test()
print('parent gets skipped')
class Child(Parent):
def test(self):
super(Parent, self).test()
print('child gets called')
This "cheats" the MRO by checking the parent's superclass instead of the child's superclass.
Lets define simple class decorator function, which creates subclass and adds 'Dec' to original class name only:
def decorate_class(klass):
new_class = type(klass.__name__ + 'Dec', (klass,), {})
return new_class
Now apply it on a simple subclass definition:
class Base(object):
def __init__(self):
print 'Base init'
#decorate_class
class MyClass(Base):
def __init__(self):
print 'MyClass init'
super(MyClass, self).__init__()
Now, if you try instantiate decorated MyClass, it will end up in an infinite loop:
c = MyClass()
# ...
# File "test.py", line 40, in __init__
# super(MyClass, self).__init__()
# RuntimeError: maximum recursion depth exceeded while calling a Python object
It seems, super can't handle this case and does not skip current class from inheritance chain.
The question, how correctly use class decorator on classes using super ?
Bonus question, how get final class from proxy-object created by super ? Ie. get object class from super(Base, self).__init__ expression, as determined parent class defining called __init__.
If you just want to change the class's .__name__ attribute, make a decorator that does that.
from __future__ import print_function
def decorate_class(klass):
klass.__name__ += 'Dec'
return klass
class Base(object):
def __init__(self):
print('Base init')
#decorate_class
class MyClass(Base):
def __init__(self):
print('MyClass init')
super(MyClass, self).__init__()
c = MyClass()
cls = c.__class__
print(cls, cls.__name__)
Python 2 output
MyClass init
Base init
<class '__main__.MyClassDec'> MyClassDec
Python 3 output
MyClass init
Base init
<class '__main__.MyClass'> MyClassDec
Note the difference in the repr of cls. (I'm not sure why you'd want to change a class's name though, it sounds like a recipe for confusion, but I guess it's ok for this simple example).
As others have said, an #decorator isn't intended to create a subclass. You can do it in Python 3 by using the arg-less form of super (i.e., super().__init__()). And you can make it work in both Python 3 and Python 2 by explicitly supplying the parent class rather than using super.
from __future__ import print_function
def decorate_class(klass):
name = klass.__name__
return type(name + 'Dec', (klass,), {})
class Base(object):
def __init__(self):
print('Base init')
#decorate_class
class MyClass(Base):
def __init__(self):
print('MyClass init')
Base.__init__(self)
c = MyClass()
cls = c.__class__
print(cls, cls.__name__)
Python 2 & 3 output
MyClass init
Base init
<class '__main__.MyClassDec'> MyClassDec
Finally, if we just call decorate_class using normal function syntax rather than as an #decorator we can use super.
from __future__ import print_function
def decorate_class(klass):
name = klass.__name__
return type(name + 'Dec', (klass,), {})
class Base(object):
def __init__(self):
print('Base init')
class MyClass(Base):
def __init__(self):
print('MyClass init')
super(MyClass, self).__init__()
MyClassDec = decorate_class(MyClass)
c = MyClassDec()
cls = c.__class__
print(cls, cls.__name__)
The output is the same as in the last version.
Since your decorator returns an entirely new class with different name, for that class MyClass object doesn't even exist. This is not the case class decorators are intended for. They are intended to add additional functionality to an existing class, not outright replacing it with some other class.
Still if you are using Python3, solution is simple -
#decorate_class
class MyClass(Base):
def __init__(self):
print 'MyClass init'
super().__init__()
Otherwise, I doubt there is any straight-forward solution, you just need to change your implementation. When you are renaming the class, you need to rewrite overwrite __init__ as well with newer name.
The problem is that your decorator creates a subclass of the original one. That means that super(Myclass) now point to... the original class itself!
I cannot even explain how the 0 arg form of super manages to do the job in Python 3, I could not find anything explicit in the reference manual. I assume it must use the class in which it is used at the time of declaration. But I cannot imagine a way to get that result in Python2.
If you want to be able to use super in the decorated class in Python 2, you should not create a derived class, but directly modify the original class in place.
For example, here is a decorator that prints a line before and after calling any method:
def decorate_class(klass):
for name, method in klass.__dict__.iteritems(): # iterate the class attributes
if isinstance(method, types.FunctionType): # identify the methods
def meth(*args, **kwargs): # define a wrapper
print "Before", name
method(*args, **kwargs)
print "After", name
setattr(klass, name, meth) # tell the class to use the wrapper
return klass
With your example it gives as expected:
>>> c = MyClass()
Before __init__
MyClass init
Base init
After __init__
In a few tutorials I've read that for __init__ method I have to do something like this:
class App:
def __init__(self):
...
However, in PySide I found this. What does the line QWidget.__init__(self) do and do I need it?
class App(QWidget):
def __init__(self):
QWidget.__init__(self)
...
Calling cls.__init__(self) (where cls can be any class), will simply call the cls's __init__ on self. Here's an example:
class Foo(object):
def __init__(self):
self.foo = True
print('Initializing object {} on Foo.__init__!'.format(self))
class Bar(object):
def __init__(self):
Foo.__init__(self)
self.bar = True
print('Initializing object {} on Bar.__init__!'.format(self))
Now creating an instance of Bar will output both __init__s:
>>> b = Bar()
Initializing object <__main__.Bar object at 0x00000000039329E8> on Foo.__init__!
Initializing object <__main__.Bar object at 0x00000000039329E8> on Bar.__init__!
Also, b now has both attributes, foo and bar.
You should rarely be calling other class's __init__, unless you're subclassing that other class. The example above is usually incorrect, since Bar is not subclassing Foo.
Even when you're subclassing a class, you shouldn't be accessing that class directly using its name, but instead use the Python's built-in function super():
# "WRONG" (usually)
class X(Y):
def __init__(self):
Y.__init__(self)
# RIGHT
class X(Y):
def __init__(self):
super().__init__() # self is omitted when using super()
This line invokes __init__ method of base class.
class App(QWidget)
App class inherits from QWidget class. Therefore, you must invoke initialize of base class when you inits instance of App class. In Python 3 you can do same thing (invoke base __init__) with
super().__init__()
In this case
class App
App class hasn't a base class.
If I make a python class which overrides another class, does __init__ get called from base class? What if I want to specify arguments for it?
Base class:
class bar(object):
def __init__(self, somearg = False):
# ...
New class
class foo(bar):
def __init__(self)
# ???
What do I want?
obj = foo() # somearg = True
No, the base class __init__ method is not called, since your derived class provides a new version of the method.
You'd call the parent __init__ method explicitly through the super() proxy and pass in the argument to set a different default:
class foo(bar):
def __init__(self)
super().__init__(somearg=True)
I know this question might be pointless but there is a reason why I am looking to do it this way. I want to call something exactly opposite to super()
class A(object):
def use_attack(self, damage, passive, spells):
#do stuff with passed parameters
#return something
def use_spell(self, name , enemy_hp):
#other code
if name == 'Enrage':
#call child method use_attack right here
class B(A):
def use_attack(self):
#bunch of code here
return super(B, self).use_attack(damage, passive, spells)
def use_spell(self, name , enemy_hp):
return super(B , self).use_attack(name ,enemy_hp)
b = B()
b.use_spell('Enrage', 100)
I have a bunch of code in class B's use_attack() method that I would not like to replicate in the parent method of use_spell() .
I would like to call the child method use_attack() in the line indicated.
I have a bunch of code in class B's use_attack() method that I would not like to replicate in the parent method of use_spell() .
Then factor that code out into a method on the parent class. This is exactly what inheritance is for. Children inherit code from parents, not the other way around.
From the python docs: "The mro attribute of the type lists the method resolution search order used by both getattr() and super()"
https://docs.python.org/3/library/functions.html#super
This should help shed some light on Inheritance and Method Resolution Order (mro).
class Foo(object):
def __init__(self):
print('Foo init called')
def call_child_method(self):
self.child_method()
class Bar(Foo):
def __init__(self):
print('Bar init called')
super().__init__()
def child_method(self):
print('Child method called')
bar = Bar()
bar.call_child_method()