前言
对于初学者而言,可能会对装饰器这个概念有点疑惑,不知道装饰器是什么?可以用来干什么?下面,就详细介绍一下Python的装饰器。
介绍
定义:装饰器其实就是一个函数,装饰器的返回值是一个函数对象。其作用大致上来说就是减少重复的代码,也许在学习的过程中体现不出来装饰器带来的方便,但是在以后的工作中就会体现出来了。
举个简单的例子,就是当我们使用Python WEB开发自己的网站时,假如每次访问网站内的某个页面时,都要判断当前用户有没有登录。这时如果不使用装饰器的话,都要在每个视图函数里面添加判断用户是否登录的代码,导致我们写的代码整体上看起来会有很多重复的代码,看起来也不会很好看;而使用装饰器就可以解决这几个问题。使用装饰器可以不需要在每个视图函数里面都添加判断用户是否登录的代码。
正文
在了解装饰器前,我们先了解两个小知识。
-
“函数名()”与“函数名”
语法 | 功能 |
函数名() | 执行函数里面的内容 |
函数名 | 函数的地址 |
如下所示:
def outfunc():
print("hello")
# 调用outfunc函数
outfunc()
# 打印outfunc函数名
print(outfunc)
# 运行结果:
# hello
# <function outfunc at 0x000001CAE0B13EE0>
-
函数名作为返回值或参数
平常编写函数时,我们可以返回一个数据类型,如返回一个整数型的数据等。除了可以返回一些Python的数据类型之外,我们还可以返回一个函数名。同时函数名也可以作为参数传参。
如下所示:
函数值作为返回值:
def outfunc():
# 内部函数
def innerfunc():
print("is innerfunc")
return innerfunc
# 调用outfunc函数
print(outfunc())
outfunc()()
# 运行结果:
# <function outfunc.<locals>.innerfunc at 0x000001F0A2E03F70>
# is innerfunc
函数名作为参数:
def hello():
print("this is hello")
def outfunc(func):
print("this is outfunc")
func()
# 调用outfunc函数
outfunc(hello)
# 运行结果:
# this is outfunc
# this is hello
了解完上述的小知识后,接下来看一下装饰器该怎么写及怎么调用。
-
装饰器编写与调用
装饰器的定义首先需要定义一个函数,并设置一个参数,这个参数是装饰器要装饰的函数名;然后在这个函数的内部定义一个内部函数,将这个内部函数作为装饰器函数的返回值;在这个内部函数里面需要调用装饰器函数传进来的函数名。
装饰器编写及调用:
# 装饰器
def outfunc(func):
# 内部函数
def innerfunc():
# 可以在此处编写调用func函数前所执行的代码
print("登录验证中")
func()
return innerfunc
# 测试函数
def test():
print("你好!")
# 装饰器的使用
#此时test为测试函数的地址
print(test)
# test为outfunc函数,并将test原先所指向的测试函数作为参数传入
test=outfunc(test)
# 此时的test 指向的是outfunc函数中的innerfunc函数
print(test)
test()
# 运行结果:
# <function test at 0x00000198E26C3F70>
# <function outfunc.<locals>.innerfunc at 0x00000198E26C1040>
# 登录验证中
# 你好!
上面就是装饰器的定义与使用,当然,假如按上面的装饰器使用方法,当我们要给100个函数使用装饰器时,那么我们要写100条“test=outfunc(test)”,这样看起来不是很好看。
所以,装饰器的另一个使用方法就是在要使用装饰器的函数前面加上“@装饰器名”就可以了,如下所示:
# 定义装饰器
def outfunc(func):
# 内部函数
def innerfunc():
# 可以在此处编写调用func函数前所执行的代码
print("登录验证中")
func()
return innerfunc
@outfunc
def test():
print("你好!")
@outfunc
def test1():
print("hello")
# 打印test
print(test)
# 打印test1
print(test1)
# 执行test()
test()
# 执行test1()
test1()
# 运行结果:
# <function outfunc.<locals>.innerfunc at 0x0000021976321040>
# <function outfunc.<locals>.innerfunc at 0x0000021976321160>
# 登录验证中
# 你好!
# 登录验证中
# hello
扩展
-
使用装饰器装饰带有参数的函数
当我们要使用装饰器装饰带有某个参数的函数时,需要在装饰器里面的内部函数添加对应的参数,同时将调用目标函数的代码同样添加对应的参数。如下所示:
# 定义装饰器 def outfunc(func): # 内部函数 def innerfunc(text): # 可以在此处编写用户是否登陆 print("登录验证中...",text) func(text) return innerfunc @outfunc def test(text): print("你好!",text) @outfunc def test1(text): print("hello",text) # 打印test print(test) # 打印test1 print(test1) # 执行test() test("111") # 执行test1() test1("222") # 运行结果: # <function outfunc.<locals>.innerfunc at 0x00000229EF7E10D0> # <function outfunc.<locals>.innerfunc at 0x00000229EF7E11F0> # 登录验证中... 111 # 你好! 111 # 登录验证中... 222 # hello 222
-
使用装饰器装饰带有不定参的函数
当我们不确定传入的参数有几个时,我们可以使用python的不定参数的表示:*args,**kwargs
名称 | 介绍 |
*args | 将传入的没有名字的参数放在元组内,将元组作为参数传入函数 |
**kwargs | 将传入的指定名字的参数放在字典里。将字典作为参数传入函数 |
# 定义装饰器
def outfunc(func):
# 内部函数
def innerfunc(*args,**kwargs):
# 可以在此处编写用户是否登陆
print("登录验证中...")
text=func(*args,**kwargs)
return text
return innerfunc
@outfunc
def test(*args,**kwargs):
return args
@outfunc
def test1(*args,**kwargs):
return kwargs
# 打印test
print(test)
# 打印test1
print(test1)
# 执行test()
ans1=test("111",121)
print(ans1)
# 执行test1()
ans2=test1(text="222",a="444")
print(ans2)
# 运行结果:
# <function outfunc.<locals>.innerfunc at 0x000001B4A42710D0>
# <function outfunc.<locals>.innerfunc at 0x000001B4A42711F0>
# 登录验证中...
# ('111', 121)
# 登录验证中...
# {'text': '222', 'a': '444'}
-
使用装饰器装饰带有返回值的函数
需要修饰带有返回值的函数时,需要在装饰器的内部函数中返回目标函数的返回值,如下:
# 定义装饰器
def outfunc(func):
# 内部函数
def innerfunc(text):
# 可以在此处编写用户是否登陆
print("登录验证中...",text)
text=func(text)
return text
return innerfunc
@outfunc
def test(text):
return "你好!"+text
@outfunc
def test1(text):
return "hello"+text
# 打印test
print(test)
# 打印test1
print(test1)
# 执行test()
ans1=test("111")
print(ans1)
# 执行test1()
ans2=test1("222")
print(ans2)
# 运行结果:
# <function outfunc.<locals>.innerfunc at 0x0000015C9F961160>
# <function outfunc.<locals>.innerfunc at 0x0000015C9F961280>
# 登录验证中... 111
# 你好!111
# 登录验证中... 222
# hello222
-
多个装饰器的使用
当我们需要多个装饰器修饰一个函数时,我们可以直接在目标函数上面添加多个“@装饰器名”。如下:
# 定义装饰器1
def outfunc(func):
# 内部函数
def innerfunc(*args,**kwargs):
# 可以在此处编写用户是否登陆
print("outfunc 的参数为:",func)
print("登录验证中...")
text=func(*args,**kwargs)
return text
return innerfunc
# 定义装饰器2
def func1(func):
# 内部函数
def innerfunc(*args,**kwargs):
print("func1 的参数为:",func)
print("登录中...")
return func(*args,**kwargs)
return innerfunc
# 添加多个装饰器
@func1
@outfunc
def test(*args,**kwargs):
return args
# 打印test
print(test)
# 执行test()
ans1=test("111",121)
print(ans1)
# 运行结果:
# <function func1.<locals>.innerfunc at 0x00000278B67E11F0>
# func1 的参数为: <function outfunc.<locals>.innerfunc at 0x00000278B67E1160>
# 登录中...
# outfunc 的参数为: <function test at 0x00000278B67E10D0>
# 登录验证中...
# ('111', 121)
上面的代码可以看出,outfunc装饰器传入的参数是目标函数,而func1装饰器传入的参数则是outfunc的内部函数。也就是说想要让哪个装饰器先执行,就让其在多个装饰器中的”最上面“。
-
带有参数的装饰器
当我们要给装饰器传值时,装饰器的传参数就不是像普通的函数传参一样直接在括号里面添加参数名。下面的代码是错误的:
# 装饰器
def outfunc(func,flag):
# 内部函数
print(flag)
def innerfunc():
# 可以在此处编写调用func函数前所执行的代码
print("登录验证中")
func()
return innerfunc
# 测试函数
@outfunc("aaa")
def test():
print("你好!")
# 装饰器的使用
print(test)
test()
# 运行结果:
# Traceback (most recent call last):
# File "d:\WorkSpace\graduate\graduate\test.py", line 29, in <module>
# @outfunc("aaa")
# TypeError: outfunc() missing 1 required positional argument: 'flag'
正确的写法是需要在装饰器的外面在嵌套一层def ,用来接受参数。如下:
# func1用于接收参数
def func1(flag):
# 装饰器
def outfunc(func):
# 内部函数
print(flag)
def innerfunc():
# 可以在此处编写调用func函数前所执行的代码
print("登录验证中")
func()
print(flag)
return innerfunc
return outfunc
# 测试函数
@func1("aaa")
def test():
print("你好!")
# 装饰器的使用
print(test)
test()
# 运行结果:
# aaa
# <function func1.<locals>.outfunc.<locals>.innerfunc at 0x0000029875D910D0>
# 登录验证中
# 你好!
# aaa
上面的代码中,需要注意的除了装饰器的写法之外,还要注意装饰器的用法,是“@装饰器最外层的函数(参数)”。从运行结果可以看出:
@func1("aaa")
# 这条语句的执行顺序是:
# 先执行func1("aaa")->然后@func1("aaa") 就变成了 @outfunc
# 也就是说,装饰器最外层的func1只是用于接收参数的,真正的装饰器时outfunc
结语:
以上就是Python装饰器的一些介绍啦,希望能够给大家带来一点帮助~
如有讲错的地方,欢迎指出~
如有讲得不好的地方,请见谅~