Pass in wrong class still works? - python

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.

Related

Calling super for higher order classes

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.

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

python 2.7 - how to call parent class constructor

I have base class like below
class FileUtil:
def __init__(self):
self.outFileDir = os.path.join(settings.MEDIA_ROOT,'processed')
if not os.path.exists(outFileDir):
os.makedirs(outFileDir)
## other methods of the class
and I am extending this class as below:
class Myfile(FileUtil):
def __init__(self, extension):
super(Myfile, self).__init__()
self.extension = 'text'
## other methods of class
But i am getting below error?
super(Myfile, self).__init__()
TypeError: super() takes at least 1 argument (0 given)
I gone through many documents and found that there are different sytex of calling super() in 2.x and 3.x. I tried both ways but getting error.
You have 2 options
old style class, you should call the super constructor directly.
class FileUtil():
def __init__(self):
pass
class Myfile(FileUtil):
def __init__(self, extension):
FileUtil.__init__(self)
new style class, inherit from object in your base class and your current call to super will be processed correctly.
class FileUtil(object):
def __init__(self):
pass
class Myfile(FileUtil):
def __init__(self, extension):
super(Myfile, self).__init__()
You may need to create the FileUtil class using the super() function as well:
class FileUtil(object):
def __init__(self):
super(FileUtil, self).__init__()
...

__init__ method in PySide

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.

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