Python闭包概念入门 闭包(Closure)是 Python 中一个重要的工具。 闭包:高阶函数中,内层函数携带外层函数中的参数、变量及其环境,一同存在的状态(即使已经离开了创造它的外层函数),被称之为闭包。 被携带的外层变量称之为:自由变量,也被形容为:外层变量被闭包捕获了。 闭包中的自由变量有两个神奇的特性: 1. 第一个特性是,自由变量在闭包存在的期间,其中的值也会一直存在。因此闭包可以持有状态。 2. 另一个特性是,闭包与闭包之间的状态是隔离的。 闭包的几个特性: 它是一个嵌套函数 它可以访问外部作用域中的自由变量 它从外层函数返回 # 在 Python 中,函数可以根据给定的参数返回一个值: def hello(name): return "hello " + name print(hello("Bob")) # 与 Python 的其他对象(如字符串、整数、列表等)一样,函数也是对象,也可以赋值给一个变量 h = hello print(hello) # 输出: <function hello at 0x108e903a0> print(h) # 输出: <function hello at 0x108e903a0> # 可以看到 hello 和 h 都指向同一个函数,而函数后加括号 h('Jack') 是对其进行了调用。 print(h('jack')) # 输出: hello jack ''' 1.函数里的函数 ''' def hi(): def bob(): return 'Bob' print('Hi ' + bob()) # 输出: Hi Bob hi() # 此时的 bob 函数的作用域在 hi 之内的。如果在全局调用 bob() 会引发错误: # bob() ''' 2.函数作为返回值 函数可以作为返回值,也可以内部定义。这种在函数里传递、嵌套、返回其他函数的情况,称之为高阶函数。 函数还可以作为其他函数的参数。 闭包:高阶函数中,内层函数携带外层函数中的参数、变量及其环境,一同存在的状态(即使已经离开了创造它的外层函数),被称之为闭包。 被携带的外层变量称之为:自由变量,也被形容为:外层变量被闭包捕获了。 ''' def cook(): def tomato(): print('I am Tomato') return tomato t = cook() t() # 输出: I am Tomato ''' 3. 闭包与自由变量: 通常来说,函数中的变量为局部变量,一但函数执行完毕,其中的变量就不可用了: ''' def cook(): food = 'apple' cook() # print(food) # 输出报错:NameError: name 'food' is not defined ''' 同样的情况到了高阶函数这里,就有点不对劲了: cook() 函数执行之后,按道理来说 food 变量就应该被销毁掉了。但实际上没有任何报错, value() 顺利的输出了 food 的值。 甚至于,即使将 cook() 函数销毁了,food 的值都还存在: ''' def cook(): food = 'apple' def wrapper(): print(food) return wrapper value = cook() value() # 输出:apple # 删除原函数 del cook value() # 输出:apple ''' 4. 参数捕获: 很多时候,我们希望闭包所捕获的自由变量可以根据不同的情况有所区分。 很简单,把它作为外层函数的参数就可以了: ''' def cook(name): def wrapper(): print('I am cooking ' + name) return wrapper apple = cook('apple') pear = cook('pear') # 外层函数的参数也可以成为自由变量,被封装到内层函数所在的环境中 # 这种局部变量起作用的特定环境,有时候被称为作用域或者域。 apple() # 输出: I am cooking apple pear() # 输出: I am cooking pear ''' 5. 函数生成: 既然外层函数可以携带参数,那被返回的内层函数当然也可以带参数: ''' def outer(x): def inner(y): print(x + y) return inner # 看到两个括号就代表进行了两次函数调用。第一个括号对应 outer 的参数 x ,第二个括号里对应 inner 的参数 y。 outer(1)(2) # 输出: 3 # 利用闭包携带参数并返回函数的这个特性,可以很方便的在一个底层的函数框架上,组装出不同的功能 # 外层函数传递的参数甚至可以是个函数。 def add(x): def inner(y): print(x + y) return inner add_one = add(1) add_ten = add(10) add_one(5) # 输出: 6 add_ten(5) # 输出: 15 ''' 6. 状态持有: 闭包中的自由变量有两个神奇的特性: 第一个特性是,自由变量在闭包存在的期间,其中的值也会一直存在。因此闭包可以持有状态。 另一个特性是,闭包与闭包之间的状态是隔离的。 以上两个特性,使得闭包像一个微型的类,因为状态持有和数据隐藏是类的基本功能。 它两在使用上的建议是:如果你的状态比较简单,那么可以用闭包来实现;相反则使用类。 ''' # 记录每次取得的分数- score 闭包打印的列表记录了每次调用的结果。 def make_score(): lis = [] def inner(x): lis.append(x) print(lis) return inner score = make_score() score(60) # 输出:[60] score(77) # 输出:[60, 77] score(88) # 输出:[60, 77, 88] # 另一个特性是,闭包与闭包之间的状态是隔离的。 def make_score(): lis = [] def inner(x): lis.append(x) print(lis) return inner first = make_score() second = make_score() first(1) # 输出:[1] first(2) # 输出:[1, 2] second(3) # 输出:[3] second(4) # 输出:[3, 4] ''' 7. 不变量状态: 在上面的例子里,闭包用 lis.append() 直接操作了自由变量。 但如果要操作的自由变量是个不变量,比如数值型、字符串等,那么记得加 nonlocal 关键字: 此关键字就是在告诉解释器:接下来的 total 不是本函数里的局部变量,你最好去闭包或是别的地方找找。 ''' # 记录成绩总分 def make_score(): total = 10 def inner(x): nonlocal total total += x print(total) return inner total = make_score() total(5) # 输出:15 ''' 8. 延迟陷阱: 闭包相关的常见陷阱 inner 是延迟执行的,直到真正调用前,都是没进行内部操作的。 ''' funcs = [] for i in range(3): def inner(): print(i) funcs.append(inner) # print(f'i is: {i}') funcs[0]() # 输出:2 funcs[1]() # 输出:2 funcs[2]() # 输出:2 # 解决方案就是用闭包将 i 的值立即捕获: funcs = [] for i in range(3): def outer(a): def inner(): print(a) return inner funcs.append(outer(i)) print(f'i is: {i}') funcs[0]() # 输出:0 funcs[1]() # 输出:1 funcs[2]() # 输出:2 """ 9. 组合函数: 用闭包实现函数的拼接 """ def compose(g, f): def inner(*args, **kwargs): return g(f(*args, **kwargs)) return inner # 被拼接函数1 def remove_lase(lis): return lis[1:] # 被拼接函数2 def remove_last(lis): return lis[:-1] # 进行函数合成 remove_last_remove_last = compose(remove_last, remove_lase) new_list = remove_last_remove_last([1, 2, 3, 4, 5]) print(new_list) # 输出:[2, 3, 4] """ 10. 柯里化 柯里化是闭包的一种应用,它将一个多参数的函数转换成一系列单参数函数。 """ # 柯里化闭包函数 def curry(f): argc = f.__code__.co_argcount f_args = [] f_kwargs = {} def g(*args, **kwargs): nonlocal f_args, f_kwargs f_args += args f_kwargs.update(kwargs) if len(f_args) + len(f_kwargs) == argc: return f(*f_args, **f_kwargs) else: return g return g # 无关紧要的原函数 def add(a, b, c): return a + b + c # c_add 是被柯里化的新函数 c_add = curry(add)
Python闭包概念入门
最新推荐文章于 2024-09-04 20:18:34 发布