0、函数嵌套
变量名解析:LEGB原则
变量名查找:
-
首先从本地(L)查找; -
本地没有找到,从上一层机构中的def或者lambda的本地作用域(E); -
从全局作用域(G)中查找; -
从内置的模块(B)中查找,第一个出现的地方查找;
nonlocal关键字:内部函数想要改变外部函数的变量,需要加nonlocal关键字,示例如下
def outer():
a = 100
def inner():
nonlocal a
b = 200
a += b
print('我是内部函数', a)
inner()
print(a)
outer()
1、闭包
1.1、概念
首先看一下维基上对闭包的解释:
在计算机科学中,闭包(英语:Closure),又称词法闭包(Lexical Closure)或函数闭包(function closures),是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。所以,有另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体。闭包在运行时可以有多个实例,不同的引用环境和相同的函数组合可以产生不同的实例。
1.2、条件
形成闭包的三个条件:
- 嵌套函数
- 内部函数引用外部函数的变量
- 返回内部函数
简单分析:通过断点查看
- 此时line值为:<function outer..inner at 0x00000000031758B0>
1.3、闭包陷阱
先看一段代码:自己思考结果
def my_func(*args):
fs = []
for i in range(3):
def func():
return i * i
fs.append(func)
return fs
fs1, fs2, fs3 = my_func()
print(fs1())
print(fs2())
print(fs3())
结果会是0 1 4 吗?并不是 结果为 4 4 4,为什么呢?
- 因为在函数my_func返回前其内部定义的函数并不是闭包函数,只是一个内部定义的函数。当然这个内部函数引用的父函数中定义的变量也不是自由变量,而只是当前block中的一个local variable。
- 在内部定义的函数func实际执行前,对局部变量j的任何改变均会影响到函数func的运行结果
正确的写法:
def my_func(*args):
fs = []
for i in range(3):
func = lambda _i=i: _i * _i
fs.append(func)
return fs
fs1, fs2, fs3 = my_func()
print(fs1())
print(fs2())
print(fs3())
2、装饰器
因为还没有学习面向对象,这里关于类装饰器相关的内容暂时不介绍,只讲解光宇函数装饰器内容。
下面我们通过模拟新房装修来认识下装饰器,你买了一座新房子(毛坯房),现在我们要对毛坯房进行装修。
开闭原则是编程中最基础、最重要的设计原则。
基本介绍:
- 一个软件实体如类,模块和函数应该对扩展开放(对于提供方来说),对修改关闭(对于使用方来说)。用抽象构建框架,用实现扩展细节。
- 当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化。
- 编程中遵循其它原则,以及使用设计模式的目的就是遵循开闭原则。
示例如下:
def decorator(func):
def wrapper():
func()
print('刷漆')
print('铺地板')
print('买家具')
print('精装修房,可以入住啦~~~')
return wrapper
@decorator
def house():
print('毛坯房。。。')
house()
那么执行顺序是怎么样的呢?
def decorator(func):
print('decorator start ...')
def wrapper():
print('wrapper start ...')
func()
print('wrapper end ...')
print('decorator end ...')
return wrapper
@decorator
def func():
print('函数执行...')
执行结果:
decorator start … decorator end …
执行顺序:加载decorator -> 加载原始func->执行decorator->func此时指向wrapper
- 执行decorator时,把原始func作为实参传给decorator的形参
2.1、带参
还是以上面装修房子为例,我们是装修队的,需要知道房屋地址。
def decorator(func):
def wrapper(address):
func(address)
print('刷漆')
print('铺地板')
print('买家具')
print('精装修房,可以入住啦~~~')
return wrapper
@decorator
def house(address):
print('房子在: {}是一座毛坯房。。。'.format(address))
house('杭州西湖')
随着业务拓展,我们不仅要装修房子,还要装修工厂,装修工厂的时候,我们还要知道厂房的面积。装修房子和装修工厂过程相似,只是接收的参数不同,那么我们能不能用相同的装饰器呢?
def decorator(func):
def wrapper(*args):
func(*args)
print('刷漆')
print('铺地板')
print('买家具')
print('精装修房,可以入住啦~~~')
return wrapper
@decorator
def house(address):
print('房子在: {} 是一座毛坯房。。。'.format(address))
@decorator
def factory(address, area):
print('工厂在: {} 是一座毛坯房,建筑面积: {}'.format(address, area))
house('杭州西湖')
factory('杭州西湖', 100)
同理,如果参数有默认值参数,那么形参需要加**kwargs:
def decorator(f):
pass
def wrapper(*args, **kwargs):
pass
f(*args, **kwargs)
pass
pass
return wrapper
2.2、带返回值
继续以装修房子为例,我们现在要对装修房子做一个预算,看看毛坯房+装修需要花费多少。
def decorator(f):
def wrapper(*args, **kwargs):
ret = f(*args, **kwargs)
print('刷漆')
print('铺地板')
print('买家具')
print('精装修房,可以入住啦~~~')
ret += 10000
return ret
return wrapper
@decorator
def house(address, area):
print('房子在: {}是一座毛坯房, 面积:{}'.format(address, area))
return 50000
cost = house('杭州西湖', 100)
print('预计花费:{}'.format(cost))
2.3、装饰器带参
简单示例:
def decrator(*dargs, **dkargs):
def wrapper(func):
def _wrapper(*args, **kargs):
print("装饰器参数:", dargs, dkargs)
print("函数参数:", args, kargs)
return func(*args, **kargs)
return _wrapper
return wrapper
@decrator(1, 2, a=1, b=2)
def f():
print('函数执行')
f()
执行顺序:加载decorator->加载f->执行decorator->返回wrapper->执行wrapper->放回_wrapper,f指向_wrapper
一般装饰器参数很少使用。
2.4、多个装饰器
def decorator_1(f):
print('decorator_1 start')
def wrapper(*args, **kwargs):
print('wrapper_1 start')
ret = f(*args, **kwargs)
print('wrapper_1 end')
return ret
print('decorator_1 end')
return wrapper
def decorator_2(f):
print('decorator_2 start')
def wrapper(*args, **kwargs):
print('wrapper_2 start')
ret = f(*args, **kwargs)
print('wrapper_2 end')
return ret
print('decorator_2 end')
return wrapper
@decorator_2
@decorator_1
def hello():
print('hello python')
hello()
可以看到,当多个装饰器装饰同一个函数时,会是一个嵌套的装饰结果,也就是说,先执行完离函数近的一个装饰器,然后再用离函数远的装饰器来装饰执行结果。
2.4、小结
装饰器功能:
- 引入日志
- 统计执行时间
- 执行函数前的预处理
- 执行函数后的清理功能
- 权限校验等场景
- 缓存
通用函数装饰器格式:
def decorator(f):
pass
def wrapper(*args, **args):
pass
ret = f(*args, **args)
pass
return ret
pass
return wrapper
|