Python的self和super

2024-10-01 09:20:37 发布

您现在位置:Python中文网/ 问答频道 /正文

在raymondhettinger在pycon2015的演讲“Super considered super speak”中,他解释了在Python中在多重继承上下文中使用super的优势。这是雷蒙德在演讲中使用的一个例子:

class DoughFactory(object):
    def get_dough(self):
        return 'insecticide treated wheat dough'


class Pizza(DoughFactory):
    def order_pizza(self, *toppings):
        print('Getting dough')
        dough = super().get_dough()
        print('Making pie with %s' % dough)
        for topping in toppings:
            print('Adding: %s' % topping)


class OrganicDoughFactory(DoughFactory):
    def get_dough(self):
        return 'pure untreated wheat dough'


class OrganicPizza(Pizza, OrganicDoughFactory):
    pass


if __name__ == '__main__':
    OrganicPizza().order_pizza('Sausage', 'Mushroom')

听众中有人asked雷蒙德谈到用self.get_dough()代替{}的区别。我不太理解Raymond的简短回答,但我编写了这个示例的两个实现,以了解它们之间的差异。两种情况下的输出相同:

^{pr2}$

如果使用self.get_dough()将类顺序从OrganicPizza(Pizza, OrganicDoughFactory)更改为OrganicPizza(OrganicDoughFactory, Pizza),您将得到以下结果:

Making pie with pure untreated wheat dough

但是,如果使用super().get_dough(),则输出如下:

Making pie with insecticide treated wheat dough

我理解Raymond解释的super()行为。但是self在多重继承场景中的预期行为是什么呢?在


Tags: selfgetdefwithclassprintmakingpie
2条回答

为了澄清一下,根据Pizza.order_pizza中的第二行和{}的定义,有四种情况:

  1. super()(Pizza, OrganicDoughFactory)(原始)'Making pie with pure untreated wheat dough'
  2. self(Pizza, OrganicDoughFactory)'Making pie with pure untreated wheat dough'
  3. super()(OrganicDoughFactory, Pizza)'Making pie with insecticide treated wheat dough'
  4. self(OrganicDoughFactory, Pizza)'Making pie with pure untreated wheat dough'

案例3。是让您感到惊讶的一个;如果我们切换继承顺序但仍然使用super,显然我们最终调用了原始的DoughFactory.get_dough。在


super真正做的是问“MRO(方法解析顺序)中的下一个?”那么OrganicPizza.mro()是什么样子的呢?在

  • (Pizza, OrganicDoughFactory)[<class '__main__.OrganicPizza'>, <class '__main__.Pizza'>, <class '__main__.OrganicDoughFactory'>, <class '__main__.DoughFactory'>, <class 'object'>]
  • (OrganicDoughFactory, Pizza)[<class '__main__.OrganicPizza'>, <class '__main__.OrganicDoughFactory'>, <class '__main__.Pizza'>, <class '__main__.DoughFactory'>, <class 'object'>]

这里的关键问题是:哪个在^{之后出现?当我们从Pizza内部调用super时,Python将在这里找到get_dough*。为1。和2。它是OrganicDoughFactory,所以我们得到了纯的,未经处理的面团,但是3块。和4。它是原始的,杀虫剂处理过的。在


那么self为什么不同呢?self总是实例,因此Python从MRO开始就开始寻找get_dough。在这两种情况下,如上所示,OrganicDoughFactory在列表中比DoughFactory早,这就是为什么self版本总是总是得到未处理的面团;self.get_dough总是解析为OrganicDoughFactory.get_dough(self)。在


*我认为在Python2.x中使用的super这两个参数的形式实际上更清楚,它是super(Pizza, self).get_dough();第一个参数是要跳过的类(即Python在MRO的其余部分中查找该类之后)。

我想分享一些关于这个问题的看法。在

如果要重写父类的get_dough()方法,则可能无法调用self.get_dough(),如下所示:

class AbdullahStore(DoughFactory):
    def get_dough(self):
        return 'Abdullah`s special ' + super().get_dough()

我认为这是实践中经常出现的情况。如果我们直接调用DoughFactory.get_dough(self),那么行为是固定的。派生AbdullahStore的类必须重写 完整方法,无法重用AbdullahStore的“附加值”。另一方面,如果我们使用super.get_dough(self),这有一种模板的味道: 在从AbdullahStore派生的任何类中,例如

^{pr2}$

我们可以用不同的方式“实例化”AbdullahStore中使用的get_dough(),方法是在MRO中截取它

class OrganicKebab(Kebab, OrganicDoughFactory):pass

它的作用是:

Kebab().order_kebab('spicy')
Making kebab with Abdullah`s special insecticide treated wheat dough and spicy sauce
OrganicKebab().order_kebab('spicy')
Making kebab with Abdullah`s special pure untreated wheat dough and spicy sauce

由于OrganicDoughFactory有一个单亲的DoughFactory,因此它被保证插入到MRO中DoughFactory之前,并因此覆盖MRO中所有前面类的方法。我花了一些时间来理解用于构造MRO的C3线性化算法。 问题是这两条规则

children come before parents
parents order is preserved

根据这个引用https://rhettinger.wordpress.com/2011/05/26/super-considered-super/不要明确地定义顺序。在类层次结构中

D->C->B->A
 \      /
    E 

(A类;B(A)类;C(B)类;E(A)类;D(C、E)类),其中E将插入MRO中?是DCBEA还是DCEBA?也许在你可以自信地回答这样的问题之前,开始在各处插入super并不是一个好主意。我还不完全确定,但我认为C3线性化,它是不含糊的,在这个例子中将选择DCBEA排序 让我们用我们做拦截的方式,毫不含糊。在

现在,我想你可以预测

class KebabNPizza(Kebab, OrganicPizza): pass
KebabNPizza().order_kebab('hot')

这是一种改良的烤肉串:

Making kebab with Abdullah`s special pure untreated wheat dough and hot sauce

但你可能花了一些时间来计算。在

当我第一次看到了一个弱的前^ ^ } DOChttps://docs.python.org/3.5/library/functions.html?highlight=super#super,它来自C++背景,它就像“哇,好吧,这里有规则,但是它怎么能工作,而不是在后面把你钉死?” 现在我对它有了更多的了解,但是仍然不愿意到处插入super。{I{I>认为基类的{16>比基类更方便。 这甚至不是说在链接super()函数时super()的极端使用。我在实践中观察到的是,每个人都用对类(而不是通用类)方便的签名编写构造函数,并使用super()来调用他们认为是基类构造函数的东西。在

相关问题 更多 >