一、命名空间和作用域
1、内置命名空间——对应Python解释器
- Python解释器一启动就可以使用的名字存储在内置命名空间中,比如print、len、list、tuple、dict==
- 内置的名字在启动解释器的时候被加载进内存里
2、全局命名空间——对应 非函数的自定义代码
- 放置了我们设置的所有变量名和函数名(所有名字)
- 在程序从上到下被执行的过程中,一次加载进内存的
3、局部命名空间——对应 自定义函数
- 是函数内部定义的变量名和函数名
- 当调用函数的时候才会产生这个名称空间,随着函数执行的结束,该命名空间被回收
4、命名空间的其他内容
- 在局部,可以使用 当前局部、全局、内置命名空间中的名字。局部命名空间根据函数个数可能有多个,其之间是隔离的,不能相互调用
- 在全局,可以使用 全局、内置命名空间的名字,但是不能使用局部命名空间的名字
- 在内置,不可以使用 局部、全部命名空间的名字
- 以上符合 依赖倒置原则:上层模块应该依赖底层模块,底层模块不能依赖上层模块
- 在正常情况下使用内置的名字,会使用内置的函数或变量;在全局定义了和内置命名空间中 同名的名字时,会使用全局的函数或变量
- ——即,如果自己有就用自己的,如果自己没有就找底层的
print(max([1,2,3])) def max(): #重新定义了max的函数 print('in max func') max() #in max func----函数内存地址(),指的是内存调用 print(max()) #None print(max) #<function max at 0x101eb48c8>----函数名的值其实是 函数的内存地址
5、作用域
- 全局作用域:作用在全局,内置和全局名字空间中的名字都属于全部作用域
- 局部作用域:作用在局部,函数-局部名字空间中的名字属于局部作用域
- 对于不可变数据类型,在局部可以查看全局作用域中的变量,但是不能直接修改,如果想要修改,需要在该函数中添加一个声明
- global:声明一个全局变量,作用域 是全局和局部的命名空间----不建议使用,不安全!!!建议使用形参、实参、返回值来修改
- nonlocal:声明一个上层局部变量,作用域是 最近一层的可找到对应名字的命名空间
-
a = 1 def func(): #a += 2 #报错 a = 2 print(a) #重新定义了一个局部变量a = 2 func() print(a) #1,全局变量1没有改变
a = 1 def func(): global a a = 2 print(a) #2 func() print(a) #2
- 两个方法:globals、locals----用的比较少
a = 1 b = 2 def func(): x = 'aaa' y = 'bbb' print(locals()) #返回局部作用域中的所有名字及其值 print(globals()) #返回全局的名字 func()
二、三元运算符???视频丢失
1、适合用三元运算的场景:
- 必须要有结果
- 必须要有if和else
- 只能是简单的情况
2、三元运算的格式: 变量 = 条件返回True的结果 if 条件 else 条件返回False的结果
def max(a,b): return a if a>b else b print(max(1,2))
三、函数的嵌套和作用域链
1、一个函数嵌套调用的示例
def max(a,b): return a if a>b else b def the_max(x,y,z): return max(x,max(y,z)) print(the_max(12,3,4))
2、函数嵌套的定义:
#一个错误的函数嵌套定义的流程 def outer(): def inner(): print('inner') outer() #调用outer函数,outer函数体只定义了inner,没有执行inner内部的函数体 #一个正确的函数嵌套定义的流程 def outer(): a = 1 def inner(): print(a) print('inner') inner() outer() #调用outer函数,outer函数体定义了inner,之后又执行了inner内部的函数体
3、作用域链:在内部函数使用变量的时候,是从小局部->大局部->全局->内置名字的过程,一级一级往上找,找到最近的一个就使用
def outer(): a = 1 def inner(): a = 1 def inner2(): nonlocal a a = a + 1 print(a,'inner2') inner2() print(a, 'inner') inner() print(a, 'outer') outer() #调用outer函数,outer函数体定义了inner,之后又执行了inner内部的函数体 '''返回结果: 2 inner2 2 inner 1 outer''' def outer(): a = 1 def inner(): #a = 1 def inner2(): nonlocal a a = a + 1 print(a,'inner2') inner2() print(a, 'inner') inner() print(a, 'outer') outer() #调用outer函数,outer函数体定义了inner,之后又执行了inner内部的函数体 '''返回结果: 2 inner2 2 inner 2 outer'''
四、函数名的本质----名字
- 函数名本质上是一个指向函数内存地址的变量名
- 函数名可以赋值
def func(): print('func') func2 = func #函数名可以赋值 func2()
- 函数名可以作为容器类型(list、tuple、dict)的元素
def func(): print('func') l = [func,func2] #函数名可以作为容器类型(list、tuple、dict)的元素 for i in l: i()
- 函数名可以作为函数的参数和返回值
def func(): print('func') def wahaha(f): #函数名可以作为函数的参数 f() return f wahaha(func) f1 = wahaha(func) #函数名可以作为函数的返回值
- 以上,函数名属于一个第一类对象,符合以下3个条件的对象就是第一类对象
- 可在运行期内创建
- 可用作函数的参数和返回值
- 可存入变量的实体
五、闭包
1、闭包的定义:嵌套函数,且内部函数调用外部函数的变量,就是闭包
def outer(): a = 1 def inner(): print(a) print(inner.__closure__) #(<cell at 0x101def648: int object at 0x1009e0980>,) outer() print(outer.__closure__) #None,返回结果没有cell at...,证明该函数不是闭包
2、常见的闭包应用形式
#一般情况下,闭包函数的调用必须通过调用外部函数来实现,会重复创建和删除,减少运行时间消耗 #使用闭包的好处在于,保护一个局部变量a不被回收,且可以随时可以通过inn的全部变量调用 def outer(): a = 1 def inner(): print(a) return inner inn = outer() inn() #将闭包函数的函数名作为返回值,并在全局使用一个变量接收,此时可以在外部调用该函数
3、闭包的一个实际应用
#import urllib #导入urllib模块 from urllib.request import urlopen def get_url(): url = 'https://www.baidu.com/' def get(): ret = urlopen(url).read() print(ret) return get get_gunc = get_url() get_gunc()