Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the difference between super() being called at the beginning or end of a method?

Tags:

python

super

I'm trying to understand how super() works. I understand what it does, but I don't understand the mechanics of what's going on behind the scenes. One thing I don't exactly understand is the difference between:

class b(a):
    def __init__(self, name, age):
        self.name=name
        self.age=age
        super(b, self).__init__(name, age)

and:

class b(a):
    def __init__(self, name, age):
        super(b, self).__init__(name, age)
        self.name=name
        self.age=age

Maybe there is no difference in these two examples, but I know there are other situations where the placement of super() matters. For example, this Django method I needed help with the other day, I was instructed to move super() above the if statement, rather than at the bottom. I would like to know why this matters.

class Mymodel(models.Model):
    photo = models.ImageField(upload_to="...", blank=True)

def save(self, *args, **kwargs):
    image_resized = kwargs.pop('image_resized',False)
    super(Mymodel, self).save(*args, **kwargs)
    if self.photo and image_resized:
        basewidth = 300
        filename = self.get_source_filename()
                image = Image.open(filename)
        wpercent = (basewidth/float(image.size[0]))
        hsize = int((float(image.size[1])*float(wpercent)))
        img = image.resize((basewidth,hsize), PIL.Image.ANTIALIAS)
        self.photo = img
        self.save(image_resized = True)
like image 445
stephan Avatar asked Jan 07 '15 18:01

stephan


2 Answers

The difference between your two versions of class b (if any) depends entirely on what the superclass (a) does. Let's just take a simpler, sharper example:

class a(object):
    def __init__(self):
        self.foo = 23

class b1(a):
    def __init__(self, name, age):
        self.foo = 42
        super(b, self).__init__()

class b2(a):
    def __init__(self, name, age):
        super(b, self).__init__()
        self.foo = 42

Class b1 first sets foo to 42 -- then (in practice) calls a.__init__, which resets the same attribute to 23. So, self.foo ends up being worth 23 after b1.__init__.

Class b2 first (in practice) calls a.__init__, which sets foo to 23 -- then, it proceeds to reset the same attribute to 42. So, self.foo ends up being worth 42 after b2.__init__.

I find this case simpler and sharper because it boils down to two assignments of different values to the same thing -- so it's very intuitive that the second assignment overrides the effect of the first one. Therefore, calling the superclass's __init__ before the subclass does its own thing means the subclass gets to override some or all of the settings done in the superclass; calling it after, means exactly the reverse.

And exactly the same reasoning applies for initialization operations that are a bit subtler than just plain assignments to self attributes: it's all about which class, sub or super, gets to tweak or overrule things done by the other (in as much as the two initializations matter at all).

In OOP it's far more common to want the subclass to "override" the superclass, than vice versa; as a consequence, the normal time to call the superclass's __init__ is right at the start of the subclass's -- that's just more idiomatic. When subtler effects are wanted, then it's fine to call the superclass's __init__ later, but in those "slightly anomalous" cases it would usually help a reader of the code to add comments explaining what's being done and why...

like image 137
Alex Martelli Avatar answered Sep 28 '22 10:09

Alex Martelli


super().__init__() executes the constructor of the class that "this"(or self in py) inherits from. So if there are actions that modifies inherited variables, or variables that need to be used in the constructor of the class that inherits, it must be called before. Otherwise it does not matter when it is called as long as you don't need it yet. Although it is best practice to always call it in the beginning of a constructor so you always know where it is called(if it is ever needed).

like image 42
BillHaggerty Avatar answered Sep 28 '22 09:09

BillHaggerty