一、函数作用域
- LEGB:L > E > G > B
- L :local 函数内部作用域
- E :encoding 函数内部与内嵌函数之间
- G :global 全局作用域
- B :build-in 内置作用域
- 概念
- global语句用以指明某个特定的变量为全局作用域,并重新绑定它;
- nonlocal语句用以指明某个特定的变量为封闭作用域,并重新绑定它;
- 如果没有使用 global语法,其赋值操作总是在最里层的作用域;
- 赋值操作不会复制数据,只是将命名绑定到对象;
- 删除也是如此:
del x
只是从局部作用域的命名空间中删除命名x
; - 事实上,所有引入新命名的操作都作用于局部作用域。
- 实例
passline = 60 # global:全局作用域
def func(val):
passline = 90 # local:函数内部作用域
if val >= passline:
print('pass')
else:
print('failed')
def in_func():
print(val) # encoding 函数内部与内嵌函数之间
in_func()
def Max(val1, val2):
return max(val1, val2) # build-in 内置作用域(max)
if __name__ == '__main__':
func(89)
# print(Max(90, 100))
二、闭包
- 概念
- 闭包:内部函数中,对enclosing作用域的变量进行引用,即内层函数引用了外层函数的局部变量或参数,然后返回内层函数的情况。
- 函数是一个对象,在内存中有一个确定的存储地址;
- 函数执行一次后,函数内部的变量,会被Python解释器回收。函数返回的变量不会被回收,会被保存在函数的
__closure__
属性中; - 使用闭包时,需要确保引用的enclosing作用域的变量,在函数返回后不能变;
- 闭包的好处:封装,实现了代码的复用。
- 示例
- 闭包:引用enclosing作用域的变量,存储在
__closure__
属性中
# coding:utf-8
passline = 60
def func(val):
print('%x' % id(val)) # val的id值
if val >= passline:
print('pass')
else:
print('failed')
def in_func(): # 引用enclosing作用域的变量
print(val)
in_func()
return in_func
f = func(89)
f()
print(f.__closure__)
10eadb050
pass
89
89
(<cell at 0x10ed6eeb8: int object at 0x10eadb050>,)
[Finished in 0.1s]
- 闭包使用场景:引用enclosing作用域的变量是一个固定值
例如:总分100时及格线是60,总分150时及格线是90,正常情况下,需要定义2个函数
def func_100(val):
passline = 60
if val >= passline:
print('pass')
else:
print('failed')
def func_150(val):
passline = 90
if val >= passline:
print('pass')
else:
print('failed')
func_100(89)
func_150(89)
pass
failed
使用闭包,定义1个函数就可以实现
def set_passline(passline):
def cmp(val):
if val >= passline:
print('pass')
else:
print('failed')
return cmp
f_100 = set_passline(60)
f_150 = set_passline(90)
f_100(89)
f_150(89)
pass
failed
- 闭包使用场景:引用enclosing作用域的变量是一个函数(及函数的参数)。举例如下:
定义了2个不同的函数:求和、求平均值,2个函数中都需要进行相同的判断逻辑:参数个数是否为0,参数类型是否是int型。
# 求和
def my_sum(*arg):
# 对参数个数和类型进行判断
if len(arg) == 0:
return 0
for val in arg:
if not isinstance(val, int):
return 0
return sum(arg)
# 求平均值
def my_avg(*arg):
# 对参数个数和类型进行判断
if len(arg) == 0:
return 0
for val in arg:
if not isinstance(val, int):
return 0
return sum(arg)/len(arg)
print(my_sum(1, 2, 3, 4, 5))
print(my_sum(1, 2, 3, 4, 5, '6'))
print(my_avg(1, 2, 3, 4, 5))
print(my_avg())
# 打印结果
15
0
3.0
0
使用闭包,定义1个实现该逻辑判断的函数
# 闭包
def my_sum(*arg):
print('my_sum arg =', arg)
return sum(arg)
def my_avg(*arg):
print('my_avg arg =', arg)
return sum(arg)/len(arg)
def dec(func):
def in_dec(*arg): # func 会放在 in_dec 的closure中
print('in_dec arg =', arg)
if len(arg) == 0:
return 0
for val in arg:
if not isinstance(val, int):
return 0
return func(*arg)
return in_dec
get_sum = dec(my_sum) # 执行步骤: get_sum --> dec return in_dec --> in_dec return my_sum
print(get_sum(1, 2, 3, 4, 5)) # 执行步骤:my_sum()
get_avg = dec(my_avg) # 执行步骤: get_sum --> dec return in_dec --> in_dec return my_avg
print(get_avg(1, 2, 3, 4, 5)) # 执行步骤:my_avg()
# 打印结果
in_dec arg = (1, 2, 3, 4, 5)
my_sum arg = (1, 2, 3, 4, 5)
15
in_dec arg = (1, 2, 3, 4, 5)
my_avg arg = (1, 2, 3, 4, 5)
3.0
三、装饰器
- 概念
- 装饰器是一个高阶函数,它接受一个函数作为参数,返回一个新函数;
- 装饰器用来装饰函数,返回一个函数对象;
- 被装饰函数,接收返回的函数对象,并且执行;
- 语法糖:
@ + function_name
- 装饰器,实际上是对闭包的使用,通过语法糖,简化了调用;
- 应用场景:定义了一个函数,想在运行时动态增加功能,但又不想改动函数本身的代码时,借助装饰器可以实现;
- 装饰器优点:极大地简化代码,避免每个函数编写重复性代码;
- 示例一
# 装饰器
def dec(func):
print('call dec')
def in_dec(*arg):
print('in_dec arg =', arg)
if len(arg) == 0:
return 0
for val in arg:
if not isinstance(val, int):
return 0
return func(*arg)
return in_dec
# 调用函数dec(),返回函数in_dec(),被函数my_sum()接收,且被执行
# 执行过程中,再次调用原来的函数my_sum()
@dec
def my_sum(*arg):
print('my_sum arg =', arg)
return sum(arg)
print(my_sum(1, 2, 3, 4, 5))
call dec
in_dec arg = (1, 2, 3, 4, 5)
my_sum arg = (1, 2, 3, 4, 5)
15
- 示例二
def deco(func):
print('call deco')
def in_deco(x, y):
print('in deco')
func(x, y)
return in_deco
# 装饰器语法糖@的执行过程:
# deco(bar) -> in_deco
# bar = in_deco
# bar() -> in_deco -> 原来的bar()
@deco
def bar(x, y):
print('in bar', x + y)
bar(1, 2)
call deco
in deco
in bar 3