语法最佳实践——类级
子类化内建类型
访问超类中的方法
super是一个内建类型,用来访问属于某个对象的超类中的特性(attribute),虽然它的使用与函数类似,但是它实际上仍然是一个内建类型。
理解python的方法解析顺序
L[MyClass(Base1, Base2)] = MyClass + merge(L[Base1], L[Base2], Base1, Base2)
C的线性化是C加上父类的线性化和父类列表的合并的总和。类的__mro__特性(只读)用来存储线性化计算的结果,计算将在类定义载入时完成。
super的缺陷
当使用了多重继承的层次结构时,再使用它是相当危险的,这主要是因为类的初始化。在python中,基类不会在__init__中被隐式地调用,所以依靠开发人员来调用它们。
- 混用super和传统调用
- 不同种类的参数
最佳实践
为了避免出现前面提到的所有问题,在Python在这个领域上取得进展之前,必须考虑一下几点:
- 应该避免多重继承
- super的使用必须一致
- 不要混用老式和新式的类
- 调用父类时必须检查类层次
描述符和属性
1 | class MyClass(object): |
描述符
描述符用来自定义在引用一个对象上的特性时所应该完成的事情,是python中复杂特性的基础,它们在内部使用,以实现属性、类、静态方法和super类等。它们是定义另一个类特性可能的访问方式的类,换句话说,一个类可以委托另一个类来管理其特性。
描述符类基于三个必须实现的特殊方法:
- __set__ 在任何特性被设置的时候调用,称其为setter
- __get__ 在任何特性被读取的时候调用,称其为getter
- __delete__ 在特性上请求del时调用
- 这些方法将在__dict__特性之前被调用
实现了__get__和__set__的描述符被称为数据描述符,只实现__get__的描述符被称为非数据描述符
内省描述符
这种描述符将检查宿主类签名,以计算一些信息
元描述符
这种描述符使用类方法本身来完成值计算
属性
属性提供了一个内建的描述符类型,它知道如何将一个特性链接到一组方法上。属性采用fget参数和三个可选的参数——fset,fdel和doc。最后一个参数可以提供用来定义一个链接到特性的docstring,就像是个方法一样。
属性简化了描述符的编写,但是在使用类的继承时必须小心处理,所创建的特性使用当前类的方法创建,而不应使用在派生类中重载的方法。这有些混乱,因为后者是大部分实现属性的语言中相当合乎逻辑的行为。
槽
python允许使用__slots__特性为指定的类设置一个静态特性列表,并且跳过每个类实例中__dict__列表的创建工作。它们用来为特性很少的类节省存储空间,因为将不在每个实例中创建__dict__。
因为任何新的特性都将在__dict__中被添加,所以这无法在派生类上工作。
元编程
元编程是在运行时通过添加新的计算功能,或者改变已有功能来改变程序行为的一种技巧。
new-style类带来了一种能力,可以通过两个特殊的方法——__new__和__metaclass__在运行时修改类和对象的定义。
__new__方法
特殊方法__new__是一个元构造程序,每当一个对象必须被factory类实例化时就将调用它。
1 | class Myclass(object): |
__new__方法必须返回一个类的实例,因此它可以在对象创建之前或之后修改类。这对于确保对象构造程序不会被设置成一个不希望的状态,或者添加一个不能被构造程序删除的初始化时有帮助的。
例如:因为__init__在子类中不会被隐式调用,所以__new__可以用来确定在整个类层次中完成初始化工作。
__metaclass__方法
元类(MetaClass)提供了在类对象通过其工厂方法(Factory)在内存中创建时进行交互的能力。它的效果与__new__类似,只不过是在类级别上运行。