在Raymond Hettinger在PyCon 2015上的演讲“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 Raymond关于使用self.get_dough()
代替super().get_dough()
的不同之处。我对Raymond的简要回答并不是很了解,但我编写了这个例子的两个实现来看看差异。两种情况的输出相同:
Getting dough
Making pie with pure untreated wheat dough
Adding: Sausage
Adding: Mushroom
如果您使用OrganicPizza(Pizza, OrganicDoughFactory)
将班级顺序从OrganicPizza(OrganicDoughFactory, Pizza)
更改为self.get_dough()
,您将获得以下结果:
Making pie with pure untreated wheat dough
但是如果你使用super().get_dough()
,这就是输出:
Making pie with insecticide treated wheat dough
我理解雷蒙德解释的super()
行为。但是self
在多继承场景中的预期行为是什么?
答案 0 :(得分:12)
为了澄清,有四种情况,基于更改Pizza.order_pizza
中的第二行和OrganicPizza
的定义:
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
内部调用Pizza
时,Python将会找到get_dough
*。对于1.和2.它OrganicDoughFactory
,所以我们得到纯净的,未经处理的面团,但对于3.和4.它是原始的,经过杀虫剂处理的DoughFactory
。
为什么self
会有所不同呢? self
始终是实例,因此Python会从MRO的开头寻找get_dough
。在这两种情况下,如上所示,OrganicDoughFactory
在列表中的位置早于DoughFactory
,这就是self
版本总是未经处理的原因; self.get_dough
始终解析为OrganicDoughFactory.get_dough(self)
。
* 我认为这在Python 2.x中使用的super
的双参数形式中更为明确,它将是super(Pizza, self).get_dough()
;第一个参数是要跳过的类(即Python在该类之后查看MRO的其余部分)。
答案 1 :(得分:2)
我想就此分享一些意见。
如果要覆盖父类的self.get_dough()
方法,则可能无法调用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
的类中,比如说
class Kebab(AbdullahStore):
def order_kebab(self, sauce):
dough = self.get_dough()
print('Making kebab with %s and %s sauce' % (dough, sauce))
我们可以实例化&#39; get_dough()
AbdullahStore
在class OrganicKebab(Kebab, OrganicDoughFactory):pass
中的使用方式不同,通过在MRO中拦截它,就像这样
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
有一个父DoughFactory
,我保证在children come before parents
parents order is preserved
之前插入MRO,因此会覆盖MRO中所有前面类的方法。我花了一些时间来理解用于构建MRO的C3线性化算法。
问题是两条规则
D->C->B->A
\ /
--E--
来自此引用https://rhettinger.wordpress.com/2011/05/26/super-considered-super/的尚未明确定义排序。在类层次结构中
super
(A类; B类(A); C类(B); E(A)类; D类(C,E))其中E将被插入MRO?是DCBEA还是DCEBA?也许在人们可以自信地回答这样的问题之前,开始在任何地方插入class KebabNPizza(Kebab, OrganicPizza): pass
KebabNPizza().order_kebab('hot')
并不是一个好主意。我仍然不完全确定,但我认为C3线性化,其中 明确并将在此示例中选择排序DCBEA,
允许我们按照我们这样做的方式进行拦截技巧,毫不含糊。
现在,我想你可以预测
的结果Making kebab with Abdullah`s special pure untreated wheat dough and hot sauce
这是一个改进的烤肉串:
super
但它可能花了你一些时间来计算。
当我第一次看到super
文档https://docs.python.org/3.5/library/functions.html?highlight=super#super弱势群体,来自C ++背景时,它就像是#34;哇,这里的规则是好的,但这是如何工作的而不是把你困在后面?&#34;。
现在我对它有了更多的了解,但仍然不愿意到处插入super()
。我认为我看到的大多数代码库只是因为super()
比基类名更方便键入。
这甚至没有谈到__init__
在链接super()
函数时的极端用法。我在实践中观察到的是,每个人都使用便于类(而不是通用的)的签名编写构造函数,并使用function count_order_no( $atts, $content = null ) {
$args = shortcode_atts( array(
'status' => 'cancelled',
), $atts );
$statuses = array_map( 'trim', explode( ',', $args['status'] ) );
$order_count = 0;
foreach ( $statuses as $status ) {
// if we didn't get a wc- prefix, add one
if ( 0 !== strpos( $status, 'wc-' ) ) {
$status = 'wc-' . $status;
}
$order_count += wp_count_posts( 'shop_order' )->$status;
}
ob_start();
echo number_format( $order_count );
return ob_get_clean();
}
add_shortcode( 'wc_order_count', 'count_order_no' );
来调用他们认为是他们的基类构造函数。