Python 高手编程系列一十八:子类化内置类型

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

相关推荐
子午44 分钟前
【鸟类识别系统】Python+TensorFlow+Django+人工智能+深度学习+卷积神经网络算法
人工智能·python·深度学习
想看一次满天星1 小时前
阿里140-n值纯算
爬虫·python·算法·网络爬虫·阿里140
Keep__Fighting1 小时前
【机器学习:逻辑回归】
人工智能·python·算法·机器学习·逻辑回归·scikit-learn·matplotlib
无限进步_1 小时前
C++运算符重载完全指南:从基础到实战应用
开发语言·数据库·c++·windows·git·github·visual studio
赖small强1 小时前
【Linux驱动开发】Linux DMA 技术详解与驱动开发实战
linux·dma·直接内存访问
斌蔚司李1 小时前
笔记本、台式机、平板二合一?Mac、Win、Linux?
linux·macos·电脑
DeeplyMind1 小时前
AMD rocr-libhsakmt分析系列6-2:共享机制-import
linux·amdgpu·dma-buf·rocm·kfd·rocr
测试19981 小时前
单元测试、系统测试、集成测试
自动化测试·软件测试·python·测试工具·职场和发展·单元测试·集成测试
Dillon Dong1 小时前
【超详细】Ubuntu 上 MySQL 5.7 升级 MySQL 8 完整指南
linux·mysql·ubuntu