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

相关推荐
老毛肚7 小时前
jeecg-boot-base-core 02 day
javascript·python
yaoxin5211237 小时前
434. Java 日期时间 API - Period 基于日期的时间段
java·开发语言·python
毒爪的小新7 小时前
Linux 环境极速部署 vLLM:从零搭建生产级大模型推理服务
linux·人工智能·ai·语言模型·vllm
鹤落晴春7 小时前
RH124问答3:从命令行管理文件
linux·运维·服务器
凡人叶枫7 小时前
Effective C++ 条款30:透彻了解 inlining 的里里外外
linux·开发语言·c++·嵌入式开发·effective c++
岁月宁静8 小时前
RAG 文档摄入全链路,从原理到生产落地
vue.js·人工智能·python
caimouse8 小时前
Reactos 第 8 章 结构化异常处理 — 8.2 系统空间的结构化异常处理
windows
JaydenAI8 小时前
[对比学习LangChain和MAF-07]如何引入人机交互的审批流程
python·ai·langchain·c#·agent·hitl·maf
Net_Walke8 小时前
【Linux系统】静态链接库与动态链接库
linux·嵌入式硬件
caimouse8 小时前
Reactos 第 7 章 视窗报文 — 7.3 Win32k 的用户空间回调机制
windows