这是我初次接触装饰器,先从初学者的角度介绍装饰器,关于装饰器的应用场景举例,后面再补充。
1.装饰器的作用
- 装饰器可以让一个函数在不做任何变动的情况下新增额外的功能。
如下代码,func_name函数是打印传入函数的函数名,f1函数就是打印一个字符串。
def func_name(func):
"""打印传入函数的函数名"""
print(func.__name__)
def f1():
print("这是待装饰的函数f1")
现在要求,每次调用一个函数的时候,都要打印该函数的函数名,那么我们可以在f1函数内部作如下修改:
def f1():
func_name(f1)
print("这是待装饰的函数")
f1() #f1 这是待装饰的函数
看似很简单,但是实际应用场景中可能有几十个函数需要作这样的修改,都在函数内部作改动是很麻烦的,也容易出错,那么我们可以利用装饰器在f1函数的外部进行修改。
2.写一个装饰器
如下代码就是对1中的f1函数作一个修改。可以看到我们定义了两个函数func_name和函数f1,其中我们用func_name函数装饰了函数f1。
那么在装饰的过程中,f1发生了怎么样的变化呢。首先我们要定义一个装饰器func_name,然后在待装饰的函数正上方输入@func_name(注意,这里是@+函数名,不带括号)。
当我们调用函数f1()时,一共发生了三件事,
- 第一,执行func_name函数,并把@下面一行的函数名称(注意,不带括号,是函数名)传给func_name,这时func_name会返回函数名称wrap(注意,是函数名,不带括号),
- 第二步,将func_name返回的函数名称wrap赋值给f1,即f1 = func_name(f1), 而func_name(f1)=wrap,所以这时,f1 = wrap(),
- 第三步,执行f1()函数,即wrap()函数,但是现在wrap()中的func()函数还是原来传入func_name的f1函数。
def func_name(func):
def wrap():
print(func.__name__)
func()
return wrap
@func_name
def f1():
print("这是待装饰的函数")
f1()# f1 这是待装饰的函数
3.带参数的装饰器
如果f1带参数呢?比如下方的f1,有一个参数args
def f1(args):
print(args)
print("这是待装饰的函数")
那么我们可以在装饰器中的wrap()和func()中也加入相同的参数。
def func_name(func):
def wrap(args): #加入参数
print(func.__name__)
func(args) #加入参数
return wrap
@func_name
def f1(args):
print(args)
print("这是待装饰的函数")
f1("我是参数")
"""
输出:
f1
我是参数
这是待装饰的函数
"""
但是如果我们需要用func_name装饰十几个函数,而十几个函数传入的参数格式、格式都不相同,怎么办呢?这时我们可以在wrap()和func()中传入动态参数*args, **kwargs,这样勿论待装饰的函数传入多少参数,装饰器都能够正确接受。
有关动态参数见:https://blog.csdn.net/lincoco49/article/details/89364034
def func_name(func):
def wrap(*args, **kwargs):#动态参数
print(func.__name__)
func(*args, **kwargs) #动态参数
return wrap
@func_name
def f1(args):#一个形参
print(args)
print("这是待装饰的函数")
@func_name
def f2(a, b): #两个形参
ret = a+b
print(ret)
f1("我是参数")
"""
f1
我是参数
这是待装饰的函数
"""
f2(1,1)
"""
f2
2
"""
4.写装饰器易错总结:
- @后面跟的是函数名,不加括号
- 装饰器返回的也是一个函数名,不加括号,加了括号就会执行函数了