装饰器
装饰器其实是一个函数,作用是装饰其他函数
装饰器的特点:
- 不改变被装饰的函数的源代码的情况下添加函数的功能
- 不改变被装饰的函数的调用方式
- 装饰器的组成方式:高阶函数+嵌套函数
高阶函数
以一个函数名(函数内存地址)为参数,此类函数就是高阶函数
譬如:
print(abs(-10)) # abs,内置函数,返回一个数的绝对值
def test(num,func): # 高阶函数,以一个函数名作为参数,传递给另一个函数
absNum = func(num) # 调用函数
print(absNum)
test(-2,abs) # 注意,这里写的是abs,是函数名,也就是函数的内存地址
嵌套函数
一个函数内部,可以嵌套函数
def outer():
def inner(): # 声明一个内部函数
x = 2
print(x)
inner() # 执行内部函数
outer()
闭包
一个内嵌函数,引用了外层函数的变量,并且外层函数的返回值是内嵌函数。这就实现了一个闭包。
说白了,内嵌函数没有重新声明某个外层变量,直接使用外层函数的变量,依靠上述这种闭包形式,可以让内嵌函数和外层函数中的变量绑定,延伸了变量的作用域
def out():
x = 1
def inner():
print(x) # inner 没有定义 x,引用了外层函数中的 x 变量
return inner # 返回 inner 函数
f = out() # f == inner,f现在是全局变量,相当于把 inner 函数提取到全局变量中来了
f() # 相当于调用了 inner(),虽然 inner 中没有定义x,但是依然能打印出x的内容,说明 inner 和外层的 x 变量绑定了。
无参数的装饰器
下面的例子实现了一个 计时器 的装饰器,可以统计一个函数的执行时间
import time
def timer(func): # func 是个函数
def inner(*args,**kwargs):
time1 = time.time()
res = func(*args,**kwargs) # 执行传递过来的 func 函数
time2 = time.time()
print(time2 - time1)
return res
return inner
@timer # python 的语法糖,会把被装饰的函数作为参数,相当于:test = timer(test);
def test():
time.sleep(2)
print('func done.')
test()
"""
1. 执行 test(),发现有装饰器语法,默认执行 test = timer(test)
2. 执行 timer(test),返回值是 inner
3. test == inner
4. 所以 test() == inner()
5. 执行 inner()
"""
有参数的装饰器
import time
def outer(name): # 有参数装饰器, 多包了一层函数
def timer(func):
def inner(*args,**kwargs):
print(name)
time1 = time.time()
res = func(*args,**kwargs) # 执行传递过来的 func 函数
time2 = time.time()
print(time2 - time1)
return res
return inner
return timer # 多包一层函数,也就多一个返回值
@outer("wang") # 先执行 outer("wang"),返回timer, 再执行 @timer,test = timer(test)
def test():
time.sleep(2)
print('func done.')
test()
"""
1. 执行 test(), 发现装饰器语法,先执行 @outer("wang")
2. outer("wang") 返回值是 timer
3. 所以相当于 @timer,此处就和无参装饰器一样了,执行 test = timer(test)
4. ...
"""
@wraps
上面的例子我们知道,装饰后的函数,实际上执行的是装饰器内层的函数inner
,因此,我们如果执行print(test.__name__)
, 得到的结果其实是:inner
。@wraps
可以让装饰后的函数,属性和被装饰前完全一致。
import time
from functools import wraps
def timer(func):
@wraps(func) # 将 func 的属性,赋值给 inner
def inner(*args,**kwargs):
time1 = time.time()
res = func(*args,**kwargs) # 执行传递过来的 func 函数
time2 = time.time()
print(time2 - time1)
return res
# inner.__name__ = func.__name__ # 上面 @wraps(func) 实际做的就是这件事情
# inner.__doc__ = func.__doc__
return inner
@timer
def test():
time.sleep(2)
print('func done.')
print(test.__name__) # 结果是:test
变量作用域
python中,每个变量都有自己的作用域。常见的作用域有:局部作用域 > 外层作用域 > 全局作用域 > python内置作用域。
作用域示例:
x = 1 # 声明在模块级别,全局变量
def outer():
x = 2 # 声明在函数内部,局部变量
def inner():
print("inner:",x) # inner函数内部没有声明x变量,在上级作用域查找
inner()
print("outer:",x)
outer()
print("global:",x)
"""
inner: 2
outer: 2
global: 1
"""
global 关键字
在函数内部使用 global 关键字,可以将局部变量提升成全局变量
x = 1 # 全局变量
def func():
global x # 在函数内部,global 关键字说明 x 是全局变量,不再局限于函数内部。
x = 2 # x 现在是全局变量,此处重新赋值
print(x)
func()
print(x) # 执行完 func(), 全局的 x 也变了
""" 结果:
2
2
"""
nonlocal 关键字
此关键字,可以让内嵌函数仅使用它的外层变量,而非全局变量。
x = 0 # 全局
def outter():
x = 1 # 内嵌函数的外层变量
def inner():
nonlocal x # 告诉程序,这是使用了外层的 x
x = 2
inner() # 执行完 inner
print(x) # x 会变成 2
outter()
print(x) # 全局的 x 依然是0