Python 的子类化内置类型非常简单。有一个叫作 object 的内置类型,它是所有内置
类型的共同祖先,也是所有没有显式指定父类的用户自定义类的共同祖先。正由于此,每
当需要实现与某个内置类型具有相似行为的类时,最好的方法就是将这个内置类型子类化。
现在,我们将向你展示一个名为 distinctdict 类的代码如下,它就使用了这种方
法。它是 Python 中普通的 dict 类型的子类。这个新类的大部分行为都与普通的 dict 相
同,但它不允许多个键对应相同的值。如果有人试图添加具有相同值的新元素,那么会引
发一个 ValueError 的子类,并给出一些帮助信息:
class DistinctError(ValueError):
“”“如果向 distinctdict 添加重复值,则引发这个错误。”“”
class distinctdict(dict):
“”“不接受重复值的字典。”“”
def setitem(self, key, value):
if value in self.values():
if (
(key in self and self[key] != value) or
key not in self
):
raise DistinctError(
“This value already exists for different key”
)
super().setitem(key, value)
下面是在交互式会话中使用 distinctdict 的示例:
my = distinctdict()
my[‘key’] = ‘value’
my[‘other_ _key’] = ‘value’
Traceback (most recent call last):
File “”, line 1, in
File “”, line 10, in _setitem __
DistinctError: This value already exists for different key
my['other key’] = ‘value2’
my
{‘key’: ‘value’, 'other _key’: ‘value2’}
如果查看现有代码,你可能会发现许多类都是对内置类型的部分实现,它们作为子类
的速度更快,代码更整洁。举个例子,list 类型用来管理序列,如果一个类需要在内部
处理序列,那么就可以对 list 进行子类化,如下所示:
class Folder(list):
def init(self, name):
self.name = name
def dir(self, nesting=0):
offset = " " * nesting
print(‘%s%s/’ % (offset, self.name))
for element in self:
if hasattr(element, ‘dir’):
element.dir(nesting + 1)
else:
print(“%s %s” % (offset, element))
下面是在交互式会话中的使用示例:
tree = Folder(‘project’)
tree.append(‘README.md’)
tree.dir()
project/
README.md
src = Folder(‘src’)
src.append(‘script.py’)
tree.append(src)
tree.dir()
project/
README.md
src/
script.py
访问超类中的方法
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 中的工作原理是很重要的。