避免不必要的面向对象编程

Python 有时被描述为面向对象编程的语言,这多少是个需要澄清的误导。在 Python 中 一切都是对象,并且能按对象的方式处理。这么说的意思是,例如函数是一等对象。 函数、类、字符串乃至类型都是 Python 对象:与其他对象一样,他们有类型,能作为 函数参数传递,并且还可能有自己的方法和属性。这样理解的话,Python 是一种面向 对象语言。

然而,与 Java 不同的是,Python 并没有将面向对象编程作为最主要的编程范式。非面向 对象的 Python 项目 (比如,使用较少甚至不使用类定义,类继承,或其它面向对象编程的 机制) 也是完全可行的。

此外,Python 管理模块与命名空间的方式提供给开发者一个自然 的方式以实现抽象层的封装和分离,这是使用面向对象最常见的原因。因而,如果业务逻辑 没有要求,Python 开发者有更多自由去选择不使用面向对象。

在一些情况下,需要避免不必要的面向对象。当我们想要将状态与功能结合起来,使用 标准类定义是有效的。但正如函数式编程所讨论的那个问题,函数式的“变量”状态与类的 状态并不相同。

在某些架构中,典型代表是 web 应用,大量 Python 进程实例被产生以响应可能同时到达的 外部请求。在这种情况下,在实例化对象内保持某些状态,即保持某些环境静态信息, 容易出现并发问题或竞态条件。有时候在对象状态的初始化 ( 通常通过 init()方法实现) 和在其方法中使用该状态之间,环境发生了变化,保留的状态可能已经过时。 举个例子,某个请求将对象加载到内存中并标记它为已读。如果同时另一个请求要删除 这个对象,删除操作可能刚好发生在第一个请求加载完该对象之后,结果就是第一个请 求标记了一个已经被删除的对象为已读。

这些问题使我们产生一个想法:使用无状态的函数是一种更好的编程范式。另一种建议 是尽量使用隐式上下文和副作用较小的函数与程序。函数的隐式上下文由函数内部访问 到的所有全局变量与持久层对象组成。副作用即函数可能使其隐式上下文发生改变。如 果函数保存或删除全局变量或持久层中数据,这种行为称为副作用。

把有隐式上下文和副作用的函数与仅包含逻辑的函数 (纯函数) 谨慎地区分开来,会带来 以下好处:

  • 纯函数的结果是确定的:给定一个输入,输出总是固定相同。
  • 当需要重构或优化时,纯函数更易于更改或替换。
  • 纯函数更容易做单元测试:很少需要复杂的上下文配置和之后的数据清除工作。
  • 纯函数更容易操作、修饰和分发。

总之,对于某些架构而言,纯函数比类和对象在构建模块时更有效率,因为他们没有任何 上下文和副作用。但显然在很多情况下,面向对象编程是有用甚至必要的。例如图形桌面 应用或游戏的开发过程中,操作的元素 (窗口、按钮、角色、车辆) 在计算机内存里拥有相对较长的生命周期。