语法最佳实践 – 类级别以上
子类化内置类型
- Python 有一个叫作 object 的内置类型,它是所有内置类型的共同祖先,也是所有没有显式指定父类的用户自定义类的共同祖先。需要实现与某个内置类型具有相似行为的类时,最好的方法就是将这个内置类型子类化。
访问超类中的方法
- super 是一个内置类,可用于访问属于某个对象的超类的属性。
- super 的简化形式(不传入任何参数)可以在方法内部使用,但 super 的使用并不限于方法。在代码中需要调用给定实例的超类方法的任何地方都可以使用它。
- super 第二个参数是可选的。如果只提供了第一个参数,那么 super 返回的是一个未绑定(unbound)类型。
- 面对多重继承模式,super 将变得难以使用,与方法解析顺序(Method Resolution Order, MRO)有关。
- Python 2 中 super() 的工作原理几乎完全相同。调用签名的唯一区别在于简化的零参数形式不可用,因此必须始终提供至少一个参数。Python 2 中的super 只适用于新式类。
- Python 3 不再保留旧式类的概念,因此,没有继承任何其他类的类都隐式地继承自 object。
- Python 的方法解析顺序基于 C3,这是为 Dylan 编程语言构建的 MRO。
- MRO 的变化是用于解决创建公共基本类型(object)所引入的问题。
- 使用 super 易犯的错误。
- 混用 super 与显式类调用。
- 初始化过程中的参数传递。
- 最佳实践。
- 应该避免多重继承。
- super 的使用必须一致:在类的层次结构中,要么全部用 super,要么全不用。
- 如果代码的使用范围包括 Python 2,在 Python 3 中也应该显式地继承自 object。
- 调用父类时必须查看类的层次结构。
高级属性访问模式
- 在一个属性前面加上
__
前缀,解释器就会立刻将其重命名,Python 提供这一特性是为了避免继承中的名称冲突,因为属性被重命名为以类名为前缀的名称。但在实践中,永远不应使用__
。如果一个属性不是公有的,约定使用_
前缀。使用描述符和 property 这些 OOP 设计的关键特性来设计一个清晰的API。 - 描述符
- 描述符允许你自定义在引用一个对象的属性时应该完成的事情。
- 描述符类基于 3 个特殊方法,这3个方法组成了描述符协议:
__set__(self, obj, type=None)
:在设置属性时将调用这一方法。__get__(self, obj, value)
:在读取属性时将调用这一方法。__delete__(self, obj)
:对属性调用 del 时将调用这一方法。
- 实现了
__get__()
和__set__()
的描述符被称为数据描述符(data descriptor)。如果只实现了__get__()
,那么就被称为非数据描述符(non-data descriptor)。 - 属性查找顺序:数据描述符优先于
__dict__
查找,而__dict__
查找优先于非数据描述符。 - 描述符的一个示例用法就是将类属性的初始化延迟到被实例访问时。
- property
- property 提供了一个内置的描述符类型,它知道如何将一个属性链接到一组方法上。property 接受 4 个可选参数:fget、fset、fdel 和 doc。最后一个参数可以用来定义一个链接到属性的 docstring,就像是一个方法一样。property 的最佳语法是使用 property 作为装饰器。
元编程
- 元编程是一种编写计算机程序的技术,这些程序可以将自己看作数据,因此你可以在运行时对它进行内省、生成和/或修改。
- 装饰器–一种元编程方法。
- 接受一个函数对象,并在运行时修改它。
- 类装饰器
- 与函数装饰器完全相同,唯一的区别在于它的返回值是一个类。
- 使用
__new__()
方法覆写实例创建过程。- 特殊方法
__new__()
是一种负责创建类实例的静态方法。无需使用 staticmethod 装饰器将其声明为静态方法。 - 只有在
__init__()
不够用的时候,使用__new__()
。
- 特殊方法
- 元类
-
元类是定义其他类(型)的一种类(型)。
-
一般语法:
- 调用内置的 type() 类可作为 class 语句的动态等效。给定名称、基类和包含属性的映射,它会创建一个新的类对象。
- 用 class 语句创建的每个类都隐式地使用 type 作为其元类。
- 虽然可以用显式调用 type() 的函数来替代元类,但通常的做法是使用继承自 type 的另一个类。
-
Python 3 中新的元类语法。
class ClassWithAMetaclass(metaclass=type): pass
- Python 2 的 class 语句不接受关键字参数,所以 Python 3 定义元类的语法会在导入时引发 SyntaxError 异常。
- Python 2 的元类没有
__prepare__()
钩子(hook)。在 Python 2 中实现这一函数不会引发任何异常,但是没有任何意义,因为它不会被调用以提供干净的命名空间对象。需要依赖更复杂的技巧来完成用__prepare__()
可以轻松完成的工作。
-
元类的使用
- 对于修改读/写属性或添加新属性之类的简单操作,可以避免使用元类,而采用更简单的解决方法,例如 property、描述符或类装饰器。
- 框架是元类真正适合使用的地方。构造 Django 的 ORM 实现。
-
使用元类易犯的错误
- 虽然类的调用签名相当严格,但 Python 并不强制要求返回参数的类型。只要它接受调用的传入参数,并且有必要的属性,那么它可以是任何内容。
-
- 一些关于代码生成的提示
- exec、eval 和 compile 3 个内置函数,用于手动执行、求值和编译任意 Python 代码。
- exec(object, globals, locals):这一函数允许你动态执行 Python 代码。
- eval(expression, globals, locals):这一函数用于对给定表达式进行求值并返回其结果。
- compile(source, filename, mode):这一函数将源代码编译成代码对象或 AST 对象。
- 抽象语法树
- Python 语法首先被转换成抽象语法树(Abstract Syntax Tree, AST),然后才被编译成字节码。这是对源代码抽象语法结构的一种树状表示。
- 使用代码生成模式的项目
- falcon 的编译路由器:falcon 是一个极简的 Python WSGI Web 框架,用于构建快速又轻量级的 API。
- Hy:Hy 是完全用 Python 编写的 Lisp 方言。
- exec、eval 和 compile 3 个内置函数,用于手动执行、求值和编译任意 Python 代码。