__init__ method in PySide - python

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.

Related

Pass in wrong class still works?

Usually super works like the following in Python:
class SubClass(MyParentClass):
def __init__(self):
super(**SubClass**, self).__init__()
But recently I found something like the following also works? No crash, behaviors as expected:
class SubClass(MyParentClass):
def __init__(self):
super(**MyParentClass**, self).__init__()
How come? What does the second case mean?
super(MyParentClass, self).__init__() Will call grandparent class (If it has one)
class BaseClass:
def __init__(self):
print("BaseClass")
class MyParentClass(BaseClass):
def __init__(self):
print("MyParentClass")
class SubClass(MyParentClass):
def __init__(self):
super(SubClass, self).__init__()
class SubClassTwo(MyParentClass):
def __init__(self):
super(MyParentClass, self).__init__()
SubClass() # output: MyParentClass
SubClassTwo() # output: BaseClass
Actually, the behaviour it's not the same.
From the documentation of super() (docs here):
Return a proxy object that delegates method calls to a parent or
sibling class of type.
So if you have:
class MyParentClass:
def __init__(self):
print('MyParentClass.__init__ called!')
class SubClass(MyParentClass):
def __init__(self):
super(MyParentClass, self).__init__()
The call:
super(MyParentClass, self).__init__()
has no effect, because MyParentClass has no parents but object.
If you call:
super(SubClass, self).__init__()
It will print:
MyParentClass.__init__ called!
Because SubClass has one parent, MyParentClass.
The documentation for super says (in part):
super([type[, object-or-type]])
Return a proxy object that delegates method calls to a parent or
sibling class of type. This is useful for accessing inherited methods
that have been overridden in a class. The search order is same as that
used by getattr() except that the type itself is skipped.
So super(MyParentClass, self) resolves to a proxy object that will pass method calls through to the parents and siblings of MyParentClass. It shouldn't be surprising that this works. If your parent class is
class MyParentClass:
def __init__(self, **kwargs):
...
super(MyParentClass, self).__init__(kwargs)
Then when you make a SubClass object, the self in the MyParentClass super call is a SubClass instance.

super not working with class decorators?

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__

How can I add to the initial definition of a python class inheriting from another class?

I'm trying to define self.data inside a class inheriting from a class
class Object():
def __init__(self):
self.data="1234"
class New_Object(Object):
# Code changing self.data here
But I ran into an issue.
class Object():
def __init__(self):
self.data="1234"
So I have the beginning class here, which is imported from elsewhere, and let's say that the class is a universal one so I can't modify the original at all.
In the original, the instance is referred to as "self" inside the class, and it is defined as self inside the definition __init__.
class New_Object(Object):
# Code changing self.data here
So if I wanted to inherit from the class Object, but define self.data inside New_Object, I thought I would have to define __init__ in New_Object, but this overrides the __init__ from New_Object
Is there any way I could do this without copypasting the __init__ from Object?
You use super to call the original implementation.
class New_Object(Object):
def __init__(self):
super(NewObject, self).__init__()
self.info = 'whatever'
That's what super is for:
class NewObject(Object):
def __init__(self):
super(NewObject, self).__init__()
# self.data exists now, and you can modify it if necessary
You can use super().__init__() to call Object.__init__() from New_Object.__init__().
What you would do:
class Object:
def __init__(self):
print("Object init")
self.data = "1234"
class New_Object(Object):
def __init__(self):
print("calling super")
super().__init__()
print("data is now", self.data)
self.data = self.data.split("3")
o = New_Object()
# calling super
# Object init
# data is now 1234
Note that you do not have to give any arguments to super(), as long as you are using Python 3.
The answer is that you call the superclass's __init__ explicitly during the subclass's __init__. This can be done either of two ways:
Object.__init__(self) # requires you to name the superclass explicitly
or
super(NewObject, self).__init__() # requires you to name the subclass explicitly
The latter also requires you to ensure that you're using "new-style" classes: in Python 3 that's always the case, but in Python 2 you must be sure to inherit from the builtin object class. In Python 3 it can actually be expressed even more simply:
super().__init__()
Personally, in most of my code the "disadvantage" of having to name the superclass explicitly is no disadvantage at all, and Object.__init__() lends transparency since it makes it absolutely clear what is being called. This is because most of my code is single-inheritance only. The super route comes into its own when you have multiple inheritance. See What does 'super' do in Python?
Python 2 example:
class Object(object):
def __init__(self):
self.data = "1234"
class NewObject:
def __init__(self):
# subclass-specific stuff
super(NewObject, self).__init__()
# more subclass-specific stuff

How to override base constructor arguments

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)

do I need to initialize the parent class when inheriting?

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.

Categories

Resources