python闭包和装饰器
2017-09-25 15:15
465 查看
一、python函数作用域LEGB
python解释器查找变量的原则(顺序):L→E→G→B
L:Local函数内部作用域
E:enclosing函数内部与内嵌函数之间
G:gobal全局作用域
B:build-in内置作用域
4000
example:
value1 = 5 def my_func(): value2 = 6 print(id(value2)) def in_func(): a = max(value1, value2) print(a) return in_func
上面示例代码中max函数为python的内建函数,在
in_func函数中,
max这个变量python解释器首先会在
in_func这个函数中查找,即local函数内部作用域中查找,然后按顺序再到E-G-B查找,最后在build-in内置作用域查找到。
二、闭包
1、概念:内部函数中对enclosing作用域的变量进行引用
上述例子中my_func函数中的内部函数
in_func引用了enclosing作用域中的
value2变量,这就叫做闭包。
由于函数执行完后变量会被回收,当执行完
my_func这个函数后,
value2变量会被回收。那么如果我们再次调用
in_func这个函数时,需用引用到
value2这个变量,是否会报错?
看以下代码:
example:
value1 = 5 def my_func(): value2 = 6 print(id(value2)) #打印value2的ID值 def in_func(): a = max(value1, value2) print(a) return in_func f = my_func() #my_func返回的是in_func,此时f指向in_func函数 f() #f()相当于in_func(),调用了infunc函数 print(f.__closure__)
ouput:
501351024 6 (<cell at 0x0000000000AF8D98: int object at 0x000000001DE20270>,)
如果内部函数引用了enclosing作用域的变量,会将变量添加到函数
__closure__的属性中去。当再次查找这个变量时,会直接去函数
__closure__的属性中查找。我们可以看到代码的输出结果第一行即为
value2的内存地址(501351024转换为16进制:1DE20270)和
__closure__属性中的int对象的地址是一样的( int object at 0x000000001DE20270)。
2、那么闭包到底有什么用呢?
我们来看下以下两段代码:(1)、
def func_100(value): passline = 60 if value >= passline: print('pass') else: print('failed') def func_150(value): passline = 90 if value >= passline: print('pass') else: print('failed') func100(59) func150(89)
(2)、
def set_passline(passline): def in_func(value): if value >= passline: print('pass') else: print('failed') return in_func f_100 = set_passline(60) #passline=60被存储在f_100的__closure__属性中 f_150 = set_passline(90) #passline=90被存储在f_150的__closure__属性中 f_100(59) f_150(89)
两段代码都能正确判断满分是100或150时,分数是否及格。但是第二段代码,由于运用闭包,代码复用性更高。
3、闭包更高级的应用
将闭包的概念中的变量变成函数,同样适用。即:内部函数中对enclosing作用域的函数进行引example:
def my_sum(*args): return(sum(args)) def my_average(*args): return sum(args)/len(args) def dec(func): def in_dec(*args): if len(args) == 0: #对参数进行判断,如果没有参数直接返回0 return 0 for i in args: if not isinstance(i, int): #对参数进行判断,如果有一个参数不是int类型,直接返回0 return 0 return func(*args) #此处对enclosing作用域的func函数进行引用 return in_dec evo_my_sum = dec(my_sum) evo_my_average = dec(my_average)
PS:此处求和函数(my_sum)和求平均值函数(my_average)只对参数是否int类型进行判断。
我们来分析下代码:
evo_my_sum = dec(my_sum)
由于函数
dec返回的是
in_dec函数
所以
evo_my_sum指向的是
in_dec函数
=》
evo_my_sum = in_dec
=》
evo_my_sum(1, 2, 3) = in_dec(1, 2, 3)
那么
in_dec(1, 2, 3)即
evo_my_sum(1, 2, 3)会对求和的参数先进行判断后,再调用
my_sum函数。
同样道理:
evo_my_average会对求平均的参数先进行判断后,再调用
my_average函数。
这样就可以对参数统一进行判断后再各自调用不同的函数。
三、装饰器
装饰器是用来装饰函数的,它返回一个函数对象。语法:@Decorator现在我们已经定义了一个
my_sum函数
def my_sum(*args): return(sum(args))
假设我们要增加
my_sum函数的功能,比如,在函数调用前对参数进行一个判断,但又不希望修改
my_sum函数的定义,这种在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)。
我们要定义一个能判断参数的decorator,如下:
def dec(func): def in_dec(*args): if len(args) == 0: return 0 for i in args: if not isinstance(i, int): return 0 return func(*args) return in_dec
按照python装饰器的语法:
@dec
def my_sum(*args): return(sum(args))
这样,调用
my_sum函数,不仅会运行
my_sum函数本身,还会在运行
my_sum函数前,对参数进行判断。
把@dec放到
my_sum函数的定义处,相当于执行了语句:
my_sum = dec(my_sum)看到这里是否觉得有点熟悉?其实这个语句和上述闭包的高级应用是一样的,只不过那部分把
my_sum改成了
evo_my_sum。
装饰器到这里还差最后一步:
my_sum函数经过装饰后,由于
dec(my_sum)返回的是
in_dec函数,此时
my_sum.__name__属性将从‘my_sum’变成‘in_dec’,为了保证此属性不变,需在
in_dec函数定义前加上语句
@functools.wraps(func),保证
my_sum.__name__属性不变。否则,有些依赖函数签名的代码执行就会出错。一个完整的decorator的写法如下:
import functools def dec(func): @functools.wraps(func) def in_dec(*args): if len(args) == 0: return 0 for i in args: if not isinstance(i, int): return 0 return func(*args) return in_dec
decorator可以增强函数的功能,定义起来虽然有点复杂,但使用起来非常灵活和方便。
以上是观看慕课网《python装饰器》以及廖雪峰教程python装饰器的总结。
相关文章推荐
- Python闭包和装饰器
- python 嵌套函数、闭包装饰器、装饰器例子
- Python之命名空间、闭包、装饰器
- Python闭包和装饰器
- python闭包与装饰器
- python学习笔记——闭包与装饰器
- Python闭包、装饰器
- 深入理解python中的闭包和装饰器
- Python 进阶_闭包 & 装饰器
- Python Enclosing作用域、闭包、装饰器
- Python函数的作用域、闭包、装饰器
- 【Python笔记】Python的几个高级语法概念浅析:lambda表达式 && 闭包 && 装饰器
- python--闭包,装饰器
- 深入理解python装饰器和闭包
- python 的内置函数,闭包,以及装饰器
- 浅显理解 Python 闭包
- Python学习笔记——闭包
- python记录_day11 闭包 迭代器
- python 闭包
- 38python 装饰器