要想玩装饰器,那就得了解一下闭包,我们不得不这样做!
闭包(Closure)
对象是附加了方法的数据。闭包是附带数据的函数。
闭包是引用了自由变量的函数。大白话说,只要某个函数中引用了一些不在当前代码全局中定义的变量,那么这个函数就是闭包函数。
def f1(x):
x1 = x
def f2(y):
y = x1 + y
return y
return f2
In [1]: def f1(x):
...: x1 = x
...: def f2(y):
...: y = x1 + y
...: return y
...: return f2
...:
In [2]: fun = f1("你好")
In [3]: fun("世界")
Out[3]: '你好世界'
In [4]: f1("武汉")("加油")
Out[4]: '武汉加油'
In [5]: f1(f1("武汉")("加油"))("!!!")
Out[5]: '武汉加油!!!'
这是怎么回事呢 ?
Python 中一切皆对象
fun = f1("你好")
中 fun
是指向 f1
函数返回的一个对象,也就是 f2
这个函数,通过传递参数 武汉
,fun
与以下代码概念一致
def f2(y):
y = "武汉" + y
return y
当 fun("世界")
大家都明白,“你好世界”
f1("武汉")("加油")
是另一种写法而已。
当调用 fun
时,f1
的作用域已经结束,x1
就成了孤鸿野鬼,它不在当前代码全局中被定义,x1
是调用 fun
被使用的变量,所以 fun
是附带数据的函数。
装饰器
装饰器语法只是一种语法糖,其返回值为另一个函数的函数,通常使用 @wrapper
语法形式来进行函数变换。装饰器通俗的讲就是某一个函数在不改动其代码的情况下而为它增加额外的功能。
现在有一段代码,它的功能是打印一些内容
def f1():
print("你好")
如果想为其增加功能,打印内容中要包含当前时间。这不很简单吗!!!
import time
def f1():
print("你好")
print(time.ctime())
或
import time
def print_time():
print(time.ctime())
def f1():
print("你好")
print_time()
No !!! 这也很不Pythonic。
语法糖
import time
def print_time(f):
def print_content():
print(time.ctime())
return f()
return print_content
@print_time
def f1():
print("你好")
其实它和以下代码原理相同
import time
def print_time(f):
def print_content():
print(time.ctime())
return f()
return print_content
def f1():
print("你好")
f1 = print_time(f1)
又是那句话 Python 中一切皆对象
f1
作为参数传递给 print_time
,print_time
返回了内层函数 print_content
的引用重新赋给 f1
,而 print_content
函数内包含了函数 f1
的引用,也就是闭包所讲的。
这个代码使时间输出在 f1
函数的上一行,如果想让先执行 f1
的内容呢
import time
def print_time(f):
def print_content():
rlt = f()
print(time.ctime())
return rlt
return print_content
def f1():
print("你好")
f1 = print_time(f1)
如果被额外增加功能的函数有参数,那上面的方法就凉了,那怎么办?
让其内层函数可以接收任意个参数就OK了
import time
def print_time(f):
def print_content(*args, **kwargs):
print(time.ctime())
return f(*args, **kwargs)
return print_content
@print_time
def f1(x=''):
print("你好" + str(x))
这要做无论被装饰的函数是否有参数都不影响,岂不是美滋滋。
带参数的装饰器
import time
def fun(a):
def print_time(f):
def print_content(*args, **kwargs):
print(time.ctime())
print(a)
return f(*args, **kwargs)
return print_content
return print_time
@fun("朋友")
def f1(x=''):
print("你好" + str(x))
带参数的装饰器无非是外面有多了一层函数,用于接收参数
这也好理解,原理和以下代码相同
import time
def fun(a):
def print_time(f):
def print_content(*args, **kwargs):
print(time.ctime())
print(a)
return f(*args, **kwargs)
return print_content
return print_time
def f1(x=''):
print("你好" + str(x))
f1 = fun("朋友")(f1)
当调用带参数的 fun
函数时返回了 print_time
函数的引用,所以他变成了 f1 = print_time(f1)
,这和上面的内容一致。
使用场景
- 授权(Authorization)
from functools import wraps
def requires_auth(f):
@wraps(f)
def decorated(*args, **kwargs):
auth = request.authorization
if not auth or not check_auth(auth.username, auth.password):
authenticate()
return f(*args, **kwargs)
return decorated
- 日志(Logging)
from functools import wraps
def logit(func):
@wraps(func)
def with_logging(*args, **kwargs):
print(func.__name__ + " was called")
return func(*args, **kwargs)
return with_logging
@logit
def addition_func(x):
"""Do some math."""
return x + x
result = addition_func(4)