返回介绍

第39章 元类

发布于 2024-01-29 22:24:14 字数 3140 浏览 0 评论 0 收藏 0

在上一章中,我们介绍了装饰器并研究了其应用的各种示例。在本书的最后一章中,我们将继续关注工具构建器,并讨论另一个高级话题:元类。

从某种意义上讲,元类只是扩展了装饰器的代码插入模式。正如我们在上一章所学到的,函数和类装饰器允许我们拦截并扩展函数调用以及类实例创建调用。以类似的思路,元类允许我们拦截并扩展类创建——它们提供了一个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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文