在 Python 中,闭包通常是定义在另一个函数内部的函数。这个内部函数抓取在其外层作用域中定义的对象,并将它们与内部函数对象本身关联起来。由此产生的组合称为闭包。在Python中,闭包可以很轻松的实现。
close closure 和 inner function
在Python中,所有的闭包都是内部函数,inner function,但不是所有的inner function都是闭包,闭包要求返回内部函数的一个对象。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 
 | def outer_function():
 x = 10
 def inner_function():
 print(x)
 return inner_function
 
 
 def outer_function():
 x = 10
 def inner_function():
 print(x)
 
 | 
作用域
inner function可以访问的变量作用域很广泛,除了简单的全局变量和inner function的局部变量,还可以访问outter function的变量,包括更新。即使outer function已经返回,inner function仍然可以访问outer function的变量。这和C++中的变量捕获范围非常不同。
| 12
 3
 4
 5
 6
 7
 8
 
 | >>> def outer_func():...     name = "Pythonista"
 ...     return lambda: print(f"Hello, {name}!")
 ...
 
 >>> greeter = outer_func()
 >>> greeter()
 Hello, Pythonista!
 
 | 
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 
 | >>> def outer_func(outer_arg):...     local_var = "Outer local variable"
 ...     def closure():
 ...         print(outer_arg)
 ...         print(local_var)
 ...         print(another_local_var)
 ...     another_local_var = "Another outer local variable"
 ...     return closure
 ...
 
 >>> closure = outer_func("Outer argument")
 
 >>> closure()
 Outer argument
 Outer local variable
 Another outer local variable
 
 | 
可变对象和不可变对象
当闭包修改不可变对象时,需要使用nonlocal关键字。
| 12
 3
 4
 5
 6
 7
 
 | >>> def outer_func():...     x = 10
 ...     def inner_func():
 ...         nonlocal x
 ...         x += 1
 ...         print(x)
 ...     return inner_func
 
 | 
可变变量则可以直接修改
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 
 | >>> def make_appender():...     items = []
 ...     def appender(new_item):
 ...         items.append(new_item)
 ...         return items
 ...     return appender
 ...
 
 >>> appender = make_appender()
 
 >>> appender("First item")
 ['First item']
 >>> appender("Second item")
 ['First item', 'Second item']
 >>> appender("Third item")
 ['First item', 'Second item', 'Third item']
 
 | 
闭包的使用场景
保存状态/状态函数
闭包可以保存状态,例如计数器。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 
 | >>> def counter():...     count = 0
 ...     def increment():
 ...         nonlocal count
 ...         count += 1
 ...         return count
 ...     return increment
 ...
 
 >>> increment = counter()
 >>> increment()
 1
 >>> increment()
 2
 >>> increment()
 3
 
 | 
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 
 | >>> def make_root_calculator(root_degree, precision=2):...     def root_calculator(number):
 ...         return round(pow(number, 1 / root_degree), precision)
 ...     return root_calculator
 ...
 
 >>> square_root = make_root_calculator(2, 4)
 >>> square_root(42)
 6.4807
 
 >>> cubic_root = make_root_calculator(3)
 >>> cubic_root(42)
 3.48
 
 | 
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 
 | >>> def cumulative_average():...     data = []
 ...     def average(value):
 ...         data.append(value)
 ...         return sum(data) / len(data)
 ...     return average
 ...
 
 >>> stream_average = cumulative_average()
 
 >>> stream_average(12)
 12.0
 >>> stream_average(13)
 12.5
 >>> stream_average(11)
 12.0
 >>> stream_average(10)
 11.5
 
 | 
回调函数
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 
 | >>> def on_click(callback):...     def handle_click(event):
 ...         callback(event)
 ...     return handle_click
 ...
 
 >>> def handle_click(event):
 ...     print(f"Clicked at {event.x}, {event.y}")
 ...
 
 >>> on_click(handle_click)
 
 | 
装饰器
| 12
 3
 4
 5
 6
 7
 
 | >>> def decorator(function):...     def closure():
 ...         print("Doing something before calling the function.")
 ...         function()
 ...         print("Doing something after calling the function.")
 ...     return closure
 ...
 
 | 
| 12
 3
 4
 5
 6
 7
 8
 9
 
 | >>> @decorator... def greet():
 ...     print("Hi, Pythonista!")
 ...
 
 >>> greet()
 Doing something before calling the function.
 Hi, Pythonista!
 Doing something after calling the function.
 
 | 
工厂函数
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 
 | >>> def make_root_calculator(root_degree, precision=2):...     def root_calculator(number):
 ...         return round(pow(number, 1 / root_degree), precision)
 ...     return root_calculator
 ...
 
 >>> square_root = make_root_calculator(2, 4)
 >>> square_root(42)
 6.4807
 
 >>> cubic_root = make_root_calculator(3)
 >>> cubic_root(42)
 3.48
 
 | 
缓存/记忆
尽管可以使用闭包实现缓存/记忆,但Python有更高效的方式。Python 标准库中内置了 memoization。如果您需要在项目中使用缓存,可以使用 functools 模块中的 @cache 或 @lru_cache。
| 12
 3
 4
 5
 6
 7
 8
 
 | >>> def memoize(function):...     cache = {}
 ...     def closure(number):
 ...         if number not in cache:
 ...             cache[number] = function(number)
 ...         return cache[number]
 ...     return closure
 ...
 
 | 
| 12
 3
 4
 
 | from time import sleep
 def slow_operation(number):
 sleep(0.5)
 
 | 
| 12
 3
 4
 5
 6
 7
 8
 
 | >>> from timeit import timeit
 >>> timeit(
 ...     "[slow_operation(number) for number in [2, 3, 4, 2, 3, 4]]",
 ...     globals=globals(),
 ...     number=1,
 ... )
 3.02610950000053
 
 | 
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 
 | >>> @memoize... def slow_operation(number):
 ...     sleep(0.5)
 ...
 
 >>> timeit(
 ...     "[slow_operation(number) for number in [2, 3, 4, 2, 3, 4]]",
 ...     globals=globals(),
 ...     number=1,
 ... )
 1.5151869590008573
 
 | 
面向对象的封装
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 
 | def Stack():_items = []
 
 def push(item):
 _items.append(item)
 
 def pop():
 return _items.pop()
 
 def closure():
 pass
 
 closure.push = push
 closure.pop = pop
 return closure
 
 | 
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 
 | >>> from stack_v2 import Stack
 >>> stack = Stack()
 >>> stack.push(1)
 >>> stack.push(2)
 >>> stack.push(3)
 
 >>> stack.pop()
 3
 
 >>> stack._items
 Traceback (most recent call last):
 ...
 AttributeError: 'function' object has no attribute '_items'
 
 |