Python进阶-II 参数陷阱、命名空间、嵌套、作用域、闭包
一、参数陷阱
在使用默认参数时,可能碰见下列情况
def show_args_trap(i, li = []): li.append(100) li[i] = 101 print(li) show_args_trap(0) show_args_trap(1) show_args_trap(2) # 显示结果为: # [101] # [101, 101] # [101, 101, 101]
分析:
大家知道:含有默认参数的,不输入该参数就使用默认的值;
在这里,第一次调用函数的时候,只传入一个参数0,这时默认参数起作用,li=[];
接着给这个列表尾部加入元素100, li = [100];
继续修改该列表,将第一元素替换成101,所以li=[101
我们接着分析第二次调用:传入参数1,此时内存中已经有了li = [101],所以默认参数没起作用! ***此处就是陷阱所在,按照默认参数的使用规则,li此时应该为空的
列表追加元素100,所以 li = [101,100];
列表修改,li = [101,101];
.........
总结:如果默认参数的值是一个可变的数据类型,那么每一次调用函数的时候,如果不传值,就公用这个数据类型的资源!
二、函数的命名空间和作用域
1、命名空间的引入
a = 1 def func(): print(a) func()
每个名字都有其作用的范围,变量名a,可以在函数定义中使用,这说明a在一定的空间内!而且有其作用的范围!
2、命名空间的分类
1)、内置命名空间 --- python解释器
就是python解释器以启动就可以使用的名字,它存储在内置命令空间里;
内置的名字在启动python解释器的时候就被加载到内存中了!
2)、全局命名空间 -- 写的代码,但不是函数中的代码
是在代码从上到下被执行的过程中,依次加入到内存中的,
它放置了我们设置的所有变量名和函数名!
3)、局部命名空间 -- 对应函数中的定义的名称
就是函数内部定义的名字;
当调用的时候,才会产生这个命名空间,随着函数的执行结束而结束,这个命名空间就消失了!''
1 # 作用域及其分类 2 ''' 全局作用域 -- 作用在全局 -- 内置和全局命名空间中的名字都属于全局作用域 -- 可使用globals() 3 局部作用域 -- 作用在局部 -- 基本在函数内部(局部命名空间中的名字属于局部作用域) -- 可使用locals()''' 4 a_b = 1 5 def self_func(): 6 #a_b += 2 # 报错:局部作用域中,不能修改全局作用域中的变量 7 global a_b # 加关键字后即可,但是不推荐经常使用,特殊情况下用! 8 a_b += 2 9 return a_b 10 self_func()
3、名字的使用范围:
1)、在局部命名空间:可以使用内置命名空间中的名字,也可以使用全局命名空间中的名字;
2)、在全局命名空间:可以使用内置。
3)、在内置命名空间:不能使用全局和局部命令空间中的名字,只能使用自己内部的名字。
1 b = 101 2 def func_inner(): 3 c = b + 100 # 会划出内置命名空间,存储c,它用到了全局命名空间中的名字b 4 return c 5 print(func_inner()) # 这里print是内置命名空间中的名字 6 7 def print(): 8 return '局部命名空间中的名字print,与内置命名空间的名字print一样' 9 print() # 在全局命名空间中,无法使用内置的命名空间中的名字,只能使用自己的和内置的!自己有,就不用调用内置的print函数
4、特殊使用范例:
def max(list): print('in max func') print(max([1,2,3]))
说明:
- 在正常情况下,直接使用内置的名字
- 当我们在全局定义了和内置名字空间中同名的名字时,会使用全局的名字
- 当我自己有的时候 我就不找我的上级要了
- 如果自己没有 就找上一级要 上一级没有再找上一级 如果内置的名字空间都没有 就报错
- 多个函数应该拥有多个独立的局部名字空间,不互相共享
# 在 “函数名()”中,函数名就是函数在内存中的地址,加()就是说要调用该函数了! print(min) # <function max at 0x000002A08BCDE620> print(min('ab11238'))
5、globals()与locals()的使用
# 对于不可变数据类型 在局部可是查看全局作用域中的变量
# 但是不能直接修改
# 如果想要修改,需要在程序的一开始添加global声明
# 如果在一个局部(函数)内声明了一个global变量,那么这个变量在局部的所有操作将对全局的变量有效
1 a = 1 2 b = 2 3 def func(): 4 x = 'aaa' 5 y = 'bbb' 6 print(locals()) # 输出什么 根据locals所在的位置 --如今在局部中,所有输出的是局部的名称 7 print(globals()) # 永远打印全局的名字 8 9 func() 10 print(globals()) 11 print(locals()) #本地的 现在全局中,所以打印的和globals()的结果一样!
三、嵌套和作用域
1、函数的嵌套的示范
def max(a, b): return a if a > b else b def max(x, y, z): c = max(x, y) return max(c, z)
2、函数的嵌套定义:
函数中定义另外一个函数,内部函数可以使用外部函数的变量
1 a = 1 # 全局变量 2 def outer(): 3 a = 1 4 def inner(): 5 b = 2 6 # global a 7 nonlocal a # 声明了一个离它最近的局部变量,对全局无效 8 a += 1 #不可变数据类型的修改,不在一个作用域内,是非法的;需要声明nonlocal,不建议使用global 9 print(a) # 一个局部包含另外一个局部 10 print('inner space') 11 def inner2(): # 没有调用,就没输出,所有的函数都是先定义后调用 12 print(a, b) 13 print('inner 2 layer') 14 inner2() 15 print('局部第二层:',a) 16 inner() # 只有在才可以调用 17 print('局部第一层:', a) 18 outer() 19 print('全局',a) 20 21 # 总结:nonlocal关键字,声明的变量仅仅对离它最近一层的局部变量有效,不管是不是在一个局部域内! 22 23 # 函数名可以赋值 24 def func(): # 函数名就是内存地址 25 print('wo men') 26 func2 = func # 函数名可以赋值 27 func2() 28 29 list = [func, func2] # 函数名可以作为容器类型的元素 30 for i in list: 31 print(123) 32 33 # 函数名可以作为函数的参数 34 def arg_fun(f): 35 f() 36 return f 37 38 arg_fun(func2) 39 40 # 函数名可以作为函数的返回值 41 def fun_as_return(f): 42 return f 43 44 fun_as_return(func2)() 45 46 # 第一类对象 47 # 1、可以在运行期创建; 48 # 2、可用作参数 49 # 3、可存入变量的实体
四、闭包
1、定义:就是嵌套函数中,内部函数调用外部函数的变量
1 def outer(): 2 a = 1 3 def inner(): 4 print(a) 5 print(inner.__closure__) # 结果:(<cell at 0x0000019EE0F37CA8: int object at 0x00000000763D5320>,) 6 outer() 7 print(outer.__closure__) # (<cell at 0x000002A5A6CC7CA8: int object at 0x00000000763D5320>,) None 8 9 # 运用闭包的常见的方式 10 def outer1(): 11 a = 1 12 def inner(): 13 print(a) 14 return inner 15 inn = outer1() 16 inn()
2、闭包的应用 爬虫初步
1 # import urllib #python自带的模块, 2 from urllib.request import urlopen 3 # 4 # # todo 改造下面的函数get_url()为闭包模式,不用每次调用的时候,频繁打开和关闭url变量,占用资源 5 # def get_url(): 6 # url = 'https://www.baidu.com/?tn=80035161_1_dg' 7 # result = urlopen(url).read() 8 # print(result) 9 # get_url() 10 11 def get_url(): 12 url = 'https://www.baidu.com/?tn=80035161_1_dg' 13 def get_url_inner(): 14 res = urlopen(url).read() 15 print(res) 16 return get_url_inner # 与def 平级,否者报错,TypeError: 'NoneType' object is not callable 17 get = get_url() 18 get()

浙公网安备 33010602011771号