在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()
代替{
如果使用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
在多重继承场景中的预期行为是什么呢?在
为了澄清一下,根据}的定义,有四种情况:
Pizza.order_pizza
中的第二行和{super()
,(Pizza, OrganicDoughFactory)
(原始):'Making pie with pure untreated wheat dough'
self
,(Pizza, OrganicDoughFactory)
:'Making pie with pure untreated wheat dough'
super()
,(OrganicDoughFactory, Pizza)
:'Making pie with insecticide treated wheat dough'
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()
,如下所示:我认为这是实践中经常出现的情况。如果我们直接调用
^{pr2}$DoughFactory.get_dough(self)
,那么行为是固定的。派生AbdullahStore
的类必须重写 完整方法,无法重用AbdullahStore
的“附加值”。另一方面,如果我们使用super.get_dough(self)
,这有一种模板的味道: 在从AbdullahStore
派生的任何类中,例如我们可以用不同的方式“实例化”
AbdullahStore
中使用的get_dough()
,方法是在MRO中截取它它的作用是:
由于
OrganicDoughFactory
有一个单亲的DoughFactory
,因此它被保证插入到MRO中DoughFactory
之前,并因此覆盖MRO中所有前面类的方法。我花了一些时间来理解用于构造MRO的C3线性化算法。 问题是这两条规则根据这个引用https://rhettinger.wordpress.com/2011/05/26/super-considered-super/不要明确地定义顺序。在类层次结构中
(A类;B(A)类;C(B)类;E(A)类;D(C、E)类),其中E将插入MRO中?是DCBEA还是DCEBA?也许在你可以自信地回答这样的问题之前,开始在各处插入
super
并不是一个好主意。我还不完全确定,但我认为C3线性化,它是不含糊的,在这个例子中将选择DCBEA排序 让我们用我们做拦截的方式,毫不含糊。在现在,我想你可以预测
这是一种改良的烤肉串:
但你可能花了一些时间来计算。在
当我第一次看到了一个弱的前^ ^super
。{I{I>认为基类的{16>比基类更方便。 这甚至不是说在链接super()
函数时super()
的极端使用。我在实践中观察到的是,每个人都用对类(而不是通用类)方便的签名编写构造函数,并使用super()
来调用他们认为是基类构造函数的东西。在相关问题 更多 >
编程相关推荐