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 "<input>", line 1, in <module>

File "<input>", 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 中的工作原理是很重要的。