Python 高手编程系列三千四百二十三:子类化内置类型

发布时间:2026/6/15 9:24:11

Python 高手编程系列三千四百二十三:子类化内置类型 Python 的子类化内置类型非常简单。有一个叫作 object 的内置类型它是所有内置类型的共同祖先也是所有没有显式指定父类的用户自定义类的共同祖先。正由于此每当需要实现与某个内置类型具有相似行为的类时最好的方法就是将这个内置类型子类化。现在我们将向你展示一个名为 distinctdict 类的代码如下它就使用了这种方法。它是 Python 中普通的 dict 类型的子类。这个新类的大部分行为都与普通的 dict 相同但它不允许多个键对应相同的值。如果有人试图添加具有相同值的新元素那么会引发一个 ValueError 的子类并给出一些帮助信息class DistinctError(ValueError):“”“如果向 distinctdict 添加重复值则引发这个错误。”“”class distinctdict(dict):“”“不接受重复值的字典。”“”defsetitem(self, key, value):if value in self.values():if ((key in self and self[key] ! value) orkey 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, inFile “”, line 10, in _setitem __DistinctError: This value already exists for different keymy[otherkey’] ‘value2’my{‘key’: ‘value’, other_key’: ‘value2’}如果查看现有代码你可能会发现许多类都是对内置类型的部分实现它们作为子类的速度更快代码更整洁。举个例子list 类型用来管理序列如果一个类需要在内部处理序列那么就可以对 list 进行子类化如下所示class Folder(list):definit(self, name):self.name namedef dir(self, nesting0):offset * nestingprint(‘%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.mdsrc Folder(‘src’)src.append(‘script.py’)tree.append(src)tree.dir()project/README.mdsrc/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 homeworkand 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:definit(self, toppings):self.toppings toppingsdefrepr(self):return “Pizza with and “.join(self.toppings)classmethoddef recommend(cls):“”“推荐任意馅料toppings的某种披萨。”””return cls([‘spam’, ‘ham’, ‘eggs’])class VikingPizza(Pizza):classmethoddef recommend(cls):“”“推荐与 super 相同的内容但多加了午餐肉spam。”“”recommended super(VikingPizza).recommend()recommended.toppings [‘spam’] * 5return recommended注意零参数的 super()形式也可用于被 classmethod 装饰器装饰的方法。在这样的方法中无参数调用的 super()被看作是仅定义了第一个参数。前面提到的使用实例很容易理解但如果面对多重继承模式super 将变得难以使用。在解释这些问题之前理解何时应避免使用 super 以及方法解析顺序Method ResolutionOrderMRO在 Python 中的工作原理是很重要的。

相关新闻