- 译者序
- 前言
- 第1章 问答环节
- 第2章 Python 如何运行程序
- 第3章 如何运行程序
- 第4章 介绍 Python 对象类型
- 第5章 数字
- 第6章 动态类型简介
- 第7章 字符串
- 第8章 列表与字典
- 第9章 元组、文件及其他
- 第10章 Python 语句简介
- 第11章 赋值、表达式和打印
- 第12章 if 测试和语法规则
- 第13章 while 和 for 循环
- 第14章 迭代器和解析,第一部分
- 第15章 文档
- 第16章 函数基础
- 第17章 作用域
- 第18章 参数
- 第19章 函数的高级话题
- 第20章 迭代和解析,第二部分
- 第21章 模块:宏伟蓝图
- 第22章 模块代码编写基础
- 第23章 模块包
- 第24章 高级模块话题
- 第25章 OOP:宏伟蓝图
- 第27章 更多实例
- 第28章 类代码编写细节
- 第29章 运算符重载
- 第30章 类的设计
- 第31章 类的高级主题
- 第32章 异常基础
- 第34章 异常对象
- 第35章 异常的设计
- 第36章 Unicode 和字节字符串
- 字符串基础知识
- Python 的字符串类型
- 文本和二进制文件
- Python 3.0 中的字符串应用
- 转换
- 编码 Unicode 字符串
- 编码非ASCII文本
- 编码和解码非ASCII文本
- 其他 Unicode 编码技术
- 转换编码
- 在 Python 2.6 中编码 Unicode 字符串
- 源文件字符集编码声明
- 使用 Python 3.0 Bytes 对象
- 序列操作
- 创建 bytes 对象的其他方式
- 混合字符串类型
- 使用 Python 3.0(和 Python 2.6)bytearray 对象
- 使用文本文件和二进制文件
- Python 3.0 中的文本和二进制模式
- 类型和内容错误匹配
- 使用 Unicode 文件
- 在 Python 3.0 中处理 BOM
- Python 2.6 中的 Unicode 文件
- Python 3.0 中其他字符串工具的变化
- Struct二进制数据模块
- pickle对象序列化模块
- XML解析工具
- 本章小结
- 本章习题
- 习题解答
- 第37章 管理属性
- 第38章 装饰器
- 第39章 元类
- 附录A 安装和配置
- 附录B 各部分练习题的解答
- 作者介绍
- 封面介绍
第39章 元类
从某种意义上讲,元类只是扩展了装饰器的代码插入模式。正如我们在上一章所学到的,函数和类装饰器允许我们拦截并扩展函数调用以及类实例创建调用。以类似的思路,元类允许我们拦截并扩展类创建——它们提供了一个API以插入在一条class语句结束时运行的额外逻辑,尽管是以与装饰器不同的方式。同样,它们提供了一种通用的协议来管理程序中的类对象。
就像本书的这一部分其他各章所讨论的主题一样,这是一个高级话题,需要有一定的基础。实际上,元类允许我们获得更高层级的控制,来控制一组类如何工作。这是一个功能强大的概念,并且元类并不是打算供大多数应用程序员使用的(或者,坦白地说,不适合懦弱之人)。
另一方面,元类为各种没有它而难以实现或不可能实现的编码模式打开了大门,并且对于那些追求编写灵活的API或编程工具供其他人使用的程序员来说,它特别有用。即便你不属于此类程序员,元类也可以教你很多有关Python的类模型的一般性知识。
和上一章一样,我们这里的部分目标只是展示比本书前面的示例更为实际的代码实例。尽管元类是一个核心主题而且并非自成一个应用领域,本章的部分目标是激发你在阅读完本书后继续研究较大的应用程序编程示例的兴趣。
要么是元类,要么不是元类
元类可能是本书中最高级的主题,如果不把Python语言算作整体的话。借用经验丰富的Python开发者Tim Peters在comp.lang.python新闻组中的话来说(Tim Peters是著名的Python座右铭"import this"的作者):
[元类]比99%的用户所担心的魔力要更深。如果你犹豫是否需要它们,那你不需要它们(真正需要元类的人,能够确定地知道需要它们,并且不需要说明为什么需要)。
换句话说,元类主要是针对那些构建API和工具供他人使用的程序员。在很多情况下(如果不是大多数的话),它们可能不是应用程序工作的最佳选择。在开发其他人将来会用的代码的时候,尤其如此。“因为某物很酷”而编写它,似乎不是一种合理的判断,除非你在做实验或者学习。
然而,元类有着各种各样广泛的潜在角色,并且知道它们何时有用是很重要的。例如,它们可能用来扩展具有跟踪、对象持久、异常日志等功能的类。它们也可以用来在运行时根据配置文件来构建类的一部分,对一个类的每个方法广泛地应用函数装饰器,验证其他的接口的一致性,等等。
在它们更宏观的化身中,元类甚至可以用来实现替代的编程模式,例如面向方面编程、数据库的对象/关系映射(ORM),等等。尽管常常有实现这些结果的替代方法(正如我们看到的,类装饰器和元类的角色常常有重合),元类提供了一种正式模型,可以裁减以完成那些任务。我们没有足够的篇幅在本章中介绍所有这些第一手的应用程序,但是,在此学完了基础知识后,你应该自行在Web上搜索以找到其他的用例。
在本书中学习元类的可能原因是,这个主题能够帮助更广泛地说明Python的类机制。尽管你可能在自己的工作中编写或重用它们,也可能不会这么做,大概理解元类也可以在很大程度上更深入地理解Python。
提高魔力层次
本书的大多数部分关注直接的应用程序编码技术,因为大多数程序员都花费时间来编写模块、函数和类来实现现实的目标。他们也使用类和创建实例,并且可能甚至做一些运算符重载,但是,他们可能不会太深入地了解类实际是如何工作的细节。
然而,在本书中我们已经看到了各种工具,它们允许我们以广泛的方式控制Python的行为,并且它们常常与Python的内部与工具构建有更多的关系,而与应用程序编程领域相关甚少:
内省属性
像__class__和__dict__这样的特殊属性允许我们查看Python对象的内部实现方面,以便更广泛地处理它们,列出对象的所有属性、显示一个类名,等等。
运算符重载方法
像__str__和__add__这样特殊命名的方法,在类中编写来拦截并提供应用于类实例的内置操作的行为,例如,打印、表达式运算符等等。它们自动运行作为对内置操作的响应,并且允许类符合期望的接口。
属性拦截方法
一类特殊的运算符重载方法提供了一种方法在实例上广泛地拦截属性访问:__getattr__、__setattr__和__getattribute__允许包装的类插入自动运行的代码,这些代码可以验证属性请求并且将它们委托给嵌入的对象。它们允许一个对象的任意数目的属性——要么是选取的属性,要么是所有的属性——在访问的时候计算。
类特性
内置函数property允许我们把代码和特殊的类属性关联起来,当获取、赋值或删除该属性的时候就自动运行代码。尽管不像前面一段所介绍的工具那样通用,特性考虑到了访问特定属性时候的自动代码调用。
类属性描述符
其实,特性只是定义根据访问自动运行函数的属性描述符的一种简洁方式。描述符允许我们在单独的类中编写__get__、__set__和__delete__处理程序方法,当分配给该类的一个实例的属性被访问的时候自动运行它们。它们提供了一种通用的方式,来插入当访问一个特定的属性时自动运行的代码,并且在一个属性的常规查找之后触发它们。
函数和类装饰器
正如我们在第38章看到的,装饰器的特殊的@可调用语法,允许我们添加当调用一个函数或创建一个类实例的时候自动运行的逻辑。这个包装器逻辑可以跟踪或计时调用,验证参数,管理类的所有实例,用诸如属性获取验证的额外行为来扩展实例,等等。装饰器语法插入名称重新绑定逻辑,在函数或类定义语句的末尾自动运行该逻辑——装饰的函数和类名重新绑定到拦截了随后调用的可调用对象。
正如本章的介绍中所提到的,元类是这些技术的延续——它们允许我们在一条class语句的末尾,插入当创建一个类对象的时候自动运行的逻辑。这个逻辑不会把类名重新绑定到一个装饰器可调用对象,而是把类自身的创建指向特定的逻辑。
换句话说,元类最终只是定义自动运行代码的另外一种方式。通过元类以及前面列出的其他工具,Python为我们提供了在各种环境中插入逻辑的方法——在运算符计算时、属性访问时、函数调用时、类实例创建时,现在是在类对象创建时。
和类装饰器不同,它通常是添加实例创建时运行的逻辑,元类在类创建时运行。同样的,它们都是通常用来管理或扩展类的钩子,而不是管理其实例。
例如,元类可以用来自动为类的所有方法添加装饰,把所有使用的类注册到一个API,自动为类添加用户接口逻辑,在文本文件中从简单声明来创建或扩展类,等等。由于我们可以控制如何创建类(并且通过它们的实例获取的行为),它们的实用性潜在地很广泛。
正如我们已经看到的,这些高级Python工具中的很多都有交叉的角色。例如,属性往往可以用特性、描述符或属性拦截方法来管理。正如我们在本章中见到的,类装饰器和元类往往可以交换使用。尽管类装饰器常常用来管理实例,它们也可以用来管理类;类似的,尽管元类设计用来扩展类构建,它们也常常插入代码来管理实例。尽管选择使用哪种技术有时候纯粹是主观的事情,但知道替代方案可以帮助我们为给定的任务挑选正确的工具。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论