前言:
好久没写文章了,今天来给大家更新一篇,最近会陆续整理,发出一下以前做的笔记
作用域:
1 2 3 4 5
| g_count = 0 def outer(): o_count = 1 def inner(): i_count = 2
|
Python 中只有模块(module),类(class)以及函数(def、lambda)才会引入新的作用域,其它的代码块
(如 if/elif/else/、try/except、for/while等)是不会引入新的作用域的,也就是说这些语句内定义的变量,外部也可以访问,
如下代码:
1 2 3 4 5
| if True: msg = 'I am from beijing' print(msg) //运行结果 I am from beijing
|
全局变量和局部变量
1 2 3 4 5 6 7 8
| total = 0
def sum(arg1, arg2): total = arg1 + arg2 return total print(total) total = sum(10, 20) print(total)
|
global 和 nonlocal关键字
当内部作用域想修改外部作用域的变量时
1 2 3 4 5 6 7 8 9 10 11 12
| num = 1 def fun1(): global num print(num) num = 123 print(num) fun1() print(num)
|
如果要修改嵌套作用域(非全局作用域)中的变量则需要 nonlocal 关键字了 ,仅读取就无所谓了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| def lazy_sum(*args): def sum(): ax = 0 for n in args: ax += n return ax
return sum
s = lazy_sum(1, 2, 3, 4, 5) print(s, type(s),s(),sep='\n')
<function lazy_sum.<locals>.sum at 0x10768ed90> <class 'function'> 15
|
进入正题:
闭包
在函数嵌套的程序结构中,如果内层函数包含对外层函数局部变量的引用,同时外层函数的返回结果又是对内层函数的引用,这就构成了一个闭包。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| def lazy_sum(*args): def sum(): ax = 0 for n in args: ax += n return ax
return sum
s = lazy_sum(1, 2, 3, 4, 5) print(s, type(s),s(),sep='\n')
<function lazy_sum.<locals>.sum at 0x10768ed90> <class 'function'> 15
|
在上面的代码中,我们在外层函数lazy_sum
中又嵌套了的内层函数sum
,sum
引用了lazy_sum
的参数,并且lazy_sum
将sum
作为返回结果。如果是按照命令式语言的规则(如C++,C#),在执行sum
函数时,会由于在其作用域内找不到args
变量而出错,但是在函数式语言中,当内嵌的函数体内有引用外部作用域的变量时,将会把定义时涉及到的引用环境和函数体打包成一个整体返回,这中程序结构就是上面说的”闭包(Closure)”。所以说,闭包就是由函数及其相关的引用环境组合而成的实体,即:闭包=函数+引用环境。
从运行结果中可以看到,当调用lazy_sum(*args)
时,返回的是内部求和函数的引用地址,当调用函数sum()
时,才真正计算求和的结果。
1 2 3 4 5 6 7
| s1 = lazy_sum(1, 2, 3, 4, 5) s2 = lazy_sum(1, 2, 3, 4, 5) print(s1==s2)
False
|
我们使 用nonlocal
关键字 对上面的代码进行更改一下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| def lazy_sum(*args): ax = 0 def sum(): nonlocal ax for n in args: ax += n return ax return sum
s = lazy_sum(1, 2, 3, 4, 5) print(s, type(s), s(), sep='\n')
<function lazy_sum.<locals>.sum at 0x000001B0D80612F0> <class 'function'> 15
|
闭包的应用场景:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| class UrlTemplate: def __init__(self, template): self.template = template
def openr(self, **kwargs): return self.template.format_map(kwargs)
def url_template(template): def openr(**kwargs): return template.format_map(kwargs) return openr
root_url = url_template('https://www.jianshu.com/search?q={name}&page={page}&type={type}')
print(root_url(name='sss', page='1', type='note'))
|
总结
使用一个内部函数或者闭包的方案通常会更优雅一些。简单来讲,一个闭包就是一个函数,只不过在函数内部带上了一个额外的变量环境。闭包关键特点就是它会记住自己被定义时的环境。因此,在我们的解决方案中,opener() 函数记住了template参数的值,并在接下来的调用中使用它。任何时候只要碰到需要给某个函数增加额外的状态信息的问题,都可以考虑使用闭包。相比将函数转换成一个类而言,闭包通常是一种更加简洁和优雅的方案。
参考链接:\
https://www.jianshu.com/p/77d78d84a69c