在方法的开头或结尾调用super()有什么区别?

时间:2015-01-07 18:25:11

标签: python super

我正在努力了解super()的工作原理。我理解它的作用,但我不了解幕后发生的事情的机制。我不太清楚的一点是:

之间的区别
class b(a):
    def __init__(self, name, age):
        self.name=name
        self.age=age
        super(b, self).__init__(name, age)

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

也许这两个例子没有区别,但我知道还有其他情况super()的位置很重要。例如,这个我需要帮助的Django方法,我被指示将super()移到if语句之上,而不是在底部。我想知道为什么这很重要。

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)

2 个答案:

答案 0 :(得分:8)

您的两个版本的类b(如果有)之间的差异完全取决于超类(a)的作用。让我们来看一个更简单,更清晰的例子:

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

b1首先将foo设置为42 - 然后(实际上)调用a.__init__,将相同的属性重置为23.因此,self.foo最终成为在b1.__init__之后价值23。

b2首先(在实践中)调用a.__init__,将foo设置为23 - 然后,它会将相同的属性重置为42.因此,{{1} } self.foo之后最终值42。

我发现这种情况更简单,更清晰,因为它归结为两个不同值的赋值给同一个东西 - 所以它非常直观,第二个赋值会覆盖第一个赋值的效果。因此,在子类之前调用​​超类的b2.__init__做自己的事情意味着子类会覆盖超类中完成的部分或全部设置;之后调用它意味着完全相反。

完全相同的推理适用于初始化操作,这些操作比__init__属性的简单赋值更微妙:它是关于哪个类,子或超级,可以调整或否决完成的事情另一方面(两个初始化都很重要)。

在OOP中,希望子类更改"覆盖"更常见。超类,反之亦然;因此,调用超类self的正常时间恰好位于子类的开头 - 这更加惯用。当需要更微妙的效果时,可以稍后调用超类__init__,但在那些"稍微异常"通常情况下,它会帮助读者的代码添加注释来解释正在做什么以及为什么......

答案 1 :(得分:1)

super().__init__()执行“this”(或py中的self)继承自的类的构造函数。因此,如果存在修改继承变量的操作,或者需要在继承的类的构造函数中使用的变量,则必须先调用它。 否则,只要你不需要它就被调用它并不重要。虽然最好总是在构造函数的开头调用它,这样你总能知道它的调用位置(如果需要的话)。

相关问题