《流畅的Python》读书笔记10(补充01): 装饰器和闭包 - 闭包实现动态平均值计算

《流畅的Python》读书笔记10(补充01): 装饰器和闭包 - 闭包实现动态平均值计算 make_averager函数是一个典型的闭包应用其核心算法逻辑是通过自由变量维护一个历史数据序列并动态计算该序列的平均值。以下是该函数的代码重现与深度拆解def make_averager(): history [] # 自由变量 def averager(new_value): history.append(new_value) return sum(history) / len(history) return averager核心算法逻辑拆解1.数据存储结构变量history一个列表list对象用于存储所有传入的历史值。该变量定义在外部函数make_averager的作用域内但对于内部函数averager而言它是一个自由变量。作用域绑定history并非averager的局部变量也非全局变量。它通过闭包机制被averager函数“捕获”并持久化。每次调用make_averager()都会创建一个全新的、独立的history列表和与之绑定的averager函数实例。2.计算过程数据更新averager函数接收一个新值new_value立即将其追加到history列表的末尾。此操作直接修改了自由变量history所引用的列表对象。均值计算计算采用即时统计算法。每次调用时通过sum(history)遍历整个列表求和再通过len(history)获取当前列表长度最后进行除法运算。其算法复杂度为 O(n)其中 n 为当前已存储的历史数据量。3.闭包机制的关键作用状态保持make_averager函数在返回averager函数后其局部作用域理应被销毁。然而由于averager函数引用了外部变量historyPython 会将该变量连同其值打包进averager函数的__closure__属性中使其生命周期与返回的函数对象绑定。内省验证可通过以下代码观察闭包的结构avg make_averager() avg(10) avg(11) print(avg.__code__.co_freevars) # 输出: (history,) print(avg.__closure__[0].cell_contents) # 输出: [10, 11]这证实了history作为自由变量被存储在闭包单元中。4.算法特性与潜在问题特性算法实现简洁且通过闭包天然实现了数据的封装每个averager实例拥有私有的history列表避免了全局变量污染。效率瓶颈随着历史数据不断积累每次计算平均值都需要遍历整个列表并求和当history非常大时性能会线性下降。内存占用该算法保存了所有原始输入数据如果数据流持续不断内存消耗将无限增长。优化方向空间与时间复杂度的权衡实现方案核心逻辑时间复杂度 (计算均值)空间复杂度适用场景当前实现 (闭包列表)存储全量历史数据每次调用重新求和O(n)O(n)历史数据量小或需要完整历史记录增量计算 (闭包计数器和)仅维护当前总和total与数据量count使用nonlocal更新O(1)O(1)仅需当前均值无需历史数据滑动窗口 (闭包双端队列)使用collections.deque限制history最大长度实现固定窗口均值O(m), m为窗口大小O(m)仅关注最近 N 个数据的移动平均值流式近似 (闭包指数加权)维护一个当前估计值新值按权重融入无需存储全部历史O(1)O(1)对实时性要求高可容忍一定延迟和近似结果增量计算优化示例def make_averager_optimized(): count 0 total 0 def averager(new_value): nonlocal count, total # 声明为自由变量以便修改 count 1 total new_value return total / count return averager此版本将计算复杂度从 O(n) 降至 O(1)空间复杂度从 O(n) 降至 O(1)但代价是丢失了完整的历史数据序列。设计模式与编程范式分析该函数是工厂函数Factory Function与闭包Closure结合的典型范例。工厂模式make_averager作为工厂每次调用都生产一个独立的、带有私有状态的averager计算实例。函数式编程它利用了函数作为一等对象的特性函数可以定义在内部、作为返回值并通过闭包实现了状态的封装与持久化避免了面向对象中类的显式使用。状态管理与将history作为实例属性的类class Averager:相比闭包方案提供了更轻量级、语法更简洁的状态封装方式尤其适用于创建单一、专注的调用对象。总结make_averager函数的核心算法是一个基于闭包的、动态维护数据序列并计算其平均值的在线算法 。其原始实现强调概念的清晰演示但存在随着数据量增长而性能下降的问题。在实际生产环境中应根据具体需求如是否需保留历史数据、对实时性的要求、内存限制等选择或实现计算复杂度更优的变体例如采用增量计算或滑动窗口策略。该函数精妙地展示了闭包如何将数据history与操作averager绑定形成一个自包含的、有状态的函数对象是理解 Python 中函数作为一等公民和闭包威力的关键示例。参考来源《流畅的Python》读书笔记10: 第二部分 函数即对象 - 装饰器和闭包