Python进阶自检清单:来自《Effective Python》的建议(二)

本系列主要介绍《Effective Python》所推荐的最佳实践,经验丰富的程序员应该对这些实践都比较熟悉了。文中有些补充信息是我自己的理解,请大家多指教!具体分析还请参考原书内容。


三:类与继承(7条建议)

1. 尽量用辅助类维护程序状态,不要用字典和元组;

如果保存内部状态的字典变得比较复杂,应该拆解为多个辅助类。

2. 简单的接口应该接受函数,而不是类的实例;

在 Python 中,函数是一等对象(first class),可以作为参数传递,我们可以利用这个特性简单实现策略模式(有需要可以参考上一个系列的文章:Python与设计模式),典型的例子是 sorted()方法。

不过,传递函数的情况只适用于比较简单的状态,如果需要保存状态,用函数也可以实现,即使用闭包,但可读性会差一些,建议还是使用辅助类。实现 __call__ 方法之后的类,实例是可调用的。

3. 以 @classmethod 形式实现类的多态;

一般只用 _init_ 方法,不够用的时候可以用这个方法添加构造器。

4. 用 super() 初始化父类;

多重继承情况下,直接调用父类进行初始化可能会出现冲突,super() 经过特殊处理,可以根据方法解析顺序进行初始化。

5. 只在使用 Mix-in 形式组件制作工具类时进行多重继承;

设计原则:多用组合,少用继承。

6. 多用 public 属性,少用 private 属性;

理论上说,Python 中的类属性前加两个下划线就是私有属性,使用者没法直接调用。但如果确实要调用,也有绕过规则的办法,所以,Python 的私有属性是不严格的。实践中一般用单下划线前缀表示受保护的属性,不是特殊情况不允许调用,也算一种社区“潜规则”。

使用私有属性的问题是,之后要继承这个类进行扩展或优化的时候,调用这个属性很麻烦,因此一般认为是得不偿失的。作者建议,只有在子类不受自己控制时,才用私有属性避免命名冲突。

7. 实现自定义容器时,应该继承 collections.abc 里的抽象基类;

作者的观点是,简单的容器还是直接继承 Python 的容器类型,比如列表或字典,比较复杂时可以继承抽象基类,主要理由是可以少写一些方法。


四:元类与属性(7条建议)

1. 用纯属性取代 get 和 set 方法;

@property | @attr_name.setter | @attr_name.getter 来替代其它语言中的 gettersetter

2. 考虑用 @property 来代替属性重构;

取消旧属性的时候,可以考虑用这个修饰器来支持调用原来的类的旧代码继续运作,但这个修饰器用得多了,显然也不合理,最后还是要重构旧代码的。

3. 用描述符来改写需要复用的 @property 方法;

描述符类即实现了__get____set____del__ 方法的类,用描述符类来改写一些属性方法比较少见,而且似乎并不直观,不过,作者的观点理论上是对的。

4. 用 __getattr____getattribute____setattr__实现按需生成的属性;

这个地方需要注意,长相上看,__getattr____setattr__比较像,但__getattr__ 是在检查实例字典,并且没有找到对应值之后才调用的,__getattribute____setattr__则会一开始就调用,所以谁和谁是一对比较让人费解。

另外,由于__getattribute____setattr__会在获取和设置属性值的时候直接调用,所以写这两个函数时要小心,避免调用自身,造成无限递归。

5. 用元类来验证子类;

元类是处理类的生成过程的,控制元类的__new__方法,可以对类的生成过程做一些验证。一般情况下,类也可以控制自己的__new__方法,但类自身的__new__方法控制的是实例的生成过程。

加载模块时,Python 会在类的 class 语句处理完毕之后,执行其元类的__new__方法。所以,错误的,或者说,不符合我们需求的类定义语句会在这时被我们验证出来。这个时间点要比调用类生成实例要早多了。

元类是一个比较高级,也比较少用的特性,这里借用《流程的Python》中引用的Tim Peters的一句话:

(元类)是深奥的知识,99%的用户都无需关注。如果你想知道是否需要使用元类,我告诉你,不需要(真正需要使用元类的人确信他们需要,无需解释原因)。

之后的两个建议也都是元类的具体使用场景,理解了元类的概念和具体执行的时间点,自然就好理解了。

6. 用元类来注册子类;

7. 用元类来注解类的属性;


发表评论

评论列表,共 0 条评论