super 是一个内置类,可用于访问属于某个对象的超类的属性。
如果你已经习惯于通过直接调用父类并传入 self 作为第一个参数来访问类的属性或
方法,那么 super 的用法会有些令人困惑。这是非常陈旧的模式,但仍然可以在一些代码
库中找到(特别是遗留项目)。参见以下代码:
class Mama: # 旧的写法
def says(self):
print('do your homework')
class Sister(Mama):
def says(self):
Mama.says(self)
print('and clean your bedroom')
在解释器会话中运行,它会给出如下结果:
>>> Sister().says()
do your homework
and clean your bedroom
重点看一下 Mama.says(self)这一行,这里我们使用刚刚提到的方法来调用超类
(即 Mama 类)的 says()方法,并将 self 作为参数传入。也就是说,调用的是属于 Mama
的 says()方法。但它的作用对象由 self 参数给出,在这个例子中是一个 Sister 实例。
而 super 的用法如下所示:
class Sister(Mama):
def says(self):
super(Sister, self).says()
print('and clean your bedroom')
或者,你也可以使用 super()调用的简化形式如下:
class Sister(Mama):
def says(self):
super().says()
print('and clean your bedroom')
super 的简化形式(不传入任何参数)可以在方法内部使用,但 super 的使用并不
限于方法。在代码中需要调用给定实例的超类方法的任何地方都可以使用它。不过,如果
super 不在方法内部使用,那么必须给出如下参数:
>>> anita = Sister()
>>> super(anita.__class__, anita).says()
do your homework
最后,关于 super 还有很重要的一点需要注意,就是它的第二个参数是可选的。如果
只提供了第一个参数,那么 super 返回的是一个未绑定(unbound)类型。这一点在与
classmethod 一起使用时特别有用,如下所示:
class Pizza:
def __init__(self, toppings):
self.toppings = toppings
def __repr__(self):
return "Pizza with " + " and ".join(self.toppings)
@classmethod
def recommend(cls):
"""推荐任意馅料(toppings)的某种披萨。"""
return cls(['spam', 'ham', 'eggs'])
class VikingPizza(Pizza):
@classmethod
def recommend(cls):
"""推荐与 super 相同的内容,但多加了午餐肉(spam)。"""
recommended = super(VikingPizza).recommend()
recommended.toppings += ['spam'] * 5
return recommended
注意,零参数的 super()形式也可用于被 classmethod 装饰器装饰的方法。在这样
的方法中无参数调用的 super()被看作是仅定义了第一个参数。
前面提到的使用实例很容易理解,但如果面对多重继承模式,super 将变得难以使用。
在解释这些问题之前,理解何时应避免使用 super 以及方法解析顺序(Method Resolution
Order,MRO)在 Python 中的工作原理是很重要的。
Python 2 中的旧式类与 super
Python 2 中 super()的工作原理几乎完全相同。调用签名的唯一区别在于简化的零参
数形式不可用,因此必须始终提供至少一个参数。
对于想要编写跨版本兼容的代码的程序员来说,另一件重要的事情是,Python 2 中的
super 只适用于新式类。在早期版本的 Python 中,所有类并没有一个共同的祖先 object。
Python 所有的 2.x 版本中都保留了旧式类,目的是为了向后兼容,所以在这些版本中,如
果类的定义中没有指定祖先,那么它就被解释为旧式类,且不能使用 super,如下所示:
class OldStyle1:
pass
class OldStyle2():
pass
Python 2 中的新式类必须显式继承 object 或其他新式类:
class NewStyleClass(object):
pass
class NewStyleClassToo(NewStyleClass):
pass
Python 3 不再保留旧式类的概念,因此,没有继承任何其他类的类都隐式地继承自
object。也就是说,显式声明某个类继承自 object 似乎是冗余的。通用的良好实践是
不包括冗余代码。但在这个例子中,只有该项目不再用于任何 Python 2 版本时,删除这些
冗余才是好的做法。如果代码想要保持 Python 的跨版本兼容,那么必须始终将 object 作
为所有基类的祖先,即使这在 Python 3 中是冗余的。不这么做的话,这些类将被解释为旧
式类,最终会导致难以诊断的问题。