一、背景引入
外部对内部可见,向内穿透,在内部可以看到外部的变量。使用的时候就近原则,函数内部变量对外是不可见的。
Python中提出了LEGB这个东西,这个就是变量解析
LEGB原则是什么呢?
二、LEGB原则
变量名解析原则原则原则就是LEGB,对名词进行解释
Local:本地作用域、局部作用域的local命名空间。函数调用时创建,调用结束消亡
Enclosing:Python2.2时引入了嵌套函数,实现了闭包,这个就是嵌套函数的外部
函数的命名空间。
Global:全局作用域,即一个模块的命名空间。模块被import时创建,解释器退出时
消亡。
Build-in:内置模块的命名空间,生命周期从python解释器启动时创建到解释器退出
时消亡。例如print(open), print和open都是内置的变量。
所以一个名词的查找顺序就是LEGB
这个的意思就是说,解释器碰到变量的时候,怎么解析它,现在L里面的范围没有的话,就继续向外找。直到找到为止,没有找到则报错。由内及外
三、函数的销毁
定义一个函数就是生成一个函数对象, 函数名指向的就是函数对象。
可以使用del语句删除函数,使其引|用计数减1。
可以使用同名标识符覆盖原有定义,本质上也是使其引用计数减1。
Python程序结束时,所有对象销毁。
函数也是对象,也不例外,是否销毁,还是看引|用计数是否减为0。
所谓的各种删除就是引用计数减1
四、匿名函数
什么叫匿名函数呢?所谓匿名就是没有名字,隐藏名字没有名称,匿名函数没有名称的函数。在python当中它不仅仅是没有名字的函数,没有名字的函数该如何调用呢?
在python 中使用Lambda表达式构建匿名函数,python中表达式不能复杂,Lambda还要另外一个名字叫做单行函数,需要一行完成。
python匿名函数的功能,是逊于其他语言的
怎么去写匿名函数呢?看下面代码
fn=lambda : 0
这个会返回一个匿名函数,用标识符fn记住了这个匿名函数
def fn(x):
x+=1
return x
把上面的fn函数改造成匿名函数
lambda x:x+1
这样两个函数就等价了
lambda 函数中不能出现=,更不能出现return
冒号后只能是表达式,表达式计算的结果作为该匿名函数return的返回值
冒号前写参数
表达式的值是做为函数返回值,进行返回。
lambda 后面加参数列表但是不要括号,有多个参数使用逗号隔开,冒号后面不能写x=+1。
因为lambda中不能出现等于号(=)
在改造一个函数
def add(x,y):
return x+y
(lambda x,y :x+y)(4,5)
这个的意思就是说,前面返回了一个函数,后面(4,5)是给的实参。这样就能
计算4+5了。
(4,5)就表示调用了
参数列表有缺省值需要向后放(x=5,y)这样写参数会报错,参数传参原则和前面
学的传参数原则一致
要这样理解匿名函数,表达式的x+y,要自己带入它完成的是return x+y
所有匿名函数的表达式都要这样考虑,自己脑子中带入一个
(lambda *,x,y: x+y)(y=4,x=5)
lambda表达式 一般不这样使用,使用在高阶函数中
y和x接收关键字传参数,(y=4,x=5) 这个表示进行调用
上面小括号表示改变优先级问题
继续看代码
[lambda *args,args](4,5,6)
用中括号括起来这个匿名函数,这个执行后会报错为什么?
[]() 这样中括号后面加小括号,解释起来就是直接调用了这个列表,列表是不能
直接调用的。
这个意思就是列表中存放了一个匿名函数对象,列表中只有着一个元素
这样继续修改代码,加了[0]
[lambda *args,args][0](4,5,6)
加这个[0]的意思就是从列表中拿到第1个元素的值,他是一个函数,这个函数,
可以加括号调用,可以接受4 5 6 三个参数,收集在了元组当中。
输出 (4, 5, 6)
函数对象后加小括号()就表示调用了
在继续看匿名函数的其他形式
lambda *args:(x for x in args))
这个会返回一个函数
(lambda *args:(x for x in args)) (1,2,3)
加了小括号()就表示调用了,会返回一个生成器对象
用next可以访问这个生成器对象
在继续看代码
(lambda *args:[x for x in args])(4,5,6)
这个会输出一个列表[4,5,6]
lambda *args:list(args)(4,5,6)
上面这两个都把参数放在列表中进行迭代,着两个的表达是一样的
(lambda *args:[args])(4,5,6) 这个输出什么?
自己认为可能返回一个列表[4,5,6],但是并不是这样的。它实际上返回的是
[(4,5,6)] 这是列表中包含了一个元组对象,上面着两个是不同的形式
(lambda *args: {x + 1 for x in args})(4, 5, 6)
输出 {5,6,7}
输出元素的位置不能确定
这个会输出什么?
(lambda *args: {x + 1 for x in args}) (range(5))
这里会报错,因为这个args没有做参数解构,range(5)就是一个元素,
这个元素被*args收集,收集之后变为一个元组,元组中就一个元素
就是range(5), range(5)是没办法加1的
(lambda *args: {x for x in args}) (range(5))
这个可以输出 {range(5)}
(lambda *args: {x + 1 for x in args}) (*range(5))
range前加 * 进行结构就可以了 这样args接收的就是
{0,1,2,3,4}
我们继续在看匿名函数的应用:
高阶函数会用到匿名函数
比如现在相对列表排序,但是数据类型不一致,该怎么办?
sorted(['a',1,'b',12,'c',28],key=str)
转类型就可以了,都变为字符串类型
现在用lambda函数来实现这个str:
这个函数能接收一个参数,返回一个字符串,一次只处理一个参数
lambda item:str(item)
这样一来不管送进来什么参数,返回的都是str类型
key后面写匿名函数函数
这两个是等价的
sorted(['a',1,'b',12,'c',28],key=str)
sorted(['a',1,'b',12,'c',28],key=lambda x:str(x))
继续引申问题,我现在非得想用整数类型比较,该怎么办?
下面这样key=int,直接语法错误,该怎么办?
sorted(['a',1,'b',12,'c',28],key=int) 语法错误
在重新写,必须用整数比较
sorted(['a',1,'b',12,'c',28],key=int)
def a(x):
if isinstance(x,str):
y=int(x,16)
else:
y=x
return y
通过对这个函数的改写来实现
sorted(['a', 1, 'b', 12, 'c', 28],
key=lambda x: int(x, 16) if isinstance(x, str) else x)
匿名函数通常用于高阶函数传参数的时候,使用lambda表达式,往往能简化代码