网站的建设和维护/一份完整的活动策划方案
上一篇文章写了python中闭包的基础使用方式,而闭包在python中较为常见的用法就是在装饰器中出现,由于前段时间看了一些装饰器的文章,因此本文想要简要介绍一下python中装饰器。需要注意的是最好先了解闭包的使用方式再来看装饰器的使用方式哟,因为装饰器实际上就是在闭包的基础上运作的。
装饰器实际上是针对python函数中的一种语法糖,使用装饰器能够在某些情况下大大减少代码量,那接下来就看一下python装饰器到底是怎样一种形式吧!
文章目录
- 在函数中定义函数
- 简单装饰器
- 使用@的装饰器
- 打印要执行的函数名时出错(functools.wraps的使用)
- 可以传入参数的装饰器
- 叮!
在函数中定义函数
在python中函数里是可以再定义函数的,先上例子:
def greeting(country='China'):def print_hello():print('hello')def print_nihao():print('你好')if country == 'China':return print_nihaoelse:return print_helloa = greeting()
a()
b = greeting('America')
b()
# 运行结果是 你好,hello
在这个例子中,能够看到将内部定义的函数print_hello在输入的国家非中国时会被return,而print_nihao则是在输入国家为中国时被return。这里的return实际上上篇文章已经讲过了,其实就是因为在python中万物皆对象,因此return print_nihao就代表返回了一个函数名,但要使函数执行,则还至少需要加个括号。这里令a = greeting()实际上是代表将greeting函数的执行结果赋值给了a(即a代表着greeting的运行结果),greeting的执行结果获取默认参数即country=‘China’,return的是print_nihao,实际上也就是说a = print_nihao,这里只是一个函数名的传递,若需要开始执行print_nihao,那么加个括号就行啦(即a(),因为这里不需要额外的参数)。执行print_nihao,就打印出了你好。
这里看起来是不是和闭包的使用方式很相近,这也是要制作装饰器的基础,因为装饰器就是要在函数中定义函数及运行函数。接下来看一下简单的装饰器。
简单装饰器
def print_name():print('牛魔王')def greeting(func): # 传递入函数名func()print('你好')greeting(print_name)
这个例子中将函数名作为参数进行传递,在greeting函数中再运行传递入的函数,实际上这就是装饰器最基础的形式啦,就是将要运行的函数传递到另外的函数中,在另个函数运行过程中调用此函数的功能,这样就不用再重复书写此函数的代码了。同时,在传入不同函数名时还能发挥不同的功能。
看一个更加完整的装饰器:
def add(a,b): # 加法return a+bdef sub(a,b): # 减法return a - bdef multiply(a,b): # 乘法return a**bdef divide(a,b): # 除法return a/bdef decorater(func):def wrapper(*args,**kargs): # 可以传入的任意参数print("计算开始") print("计算结果为:",func(*args,**kargs)) # 调用传入的函数,并打印结果print("计算结束")return wrapper # 返回内函数ad = decorater(add) # ad此时代表的是wrapper函数,后续调用时通过wrapper传入参数给add函数
ad(4,5)
mu = decorater(multiply)
mu(4,5)
su = decorater(sub)
su(4,5)
di = decorater(divide)
di(4,5)
这个结果我就不展示啦,其实就是计算开始加计算结果再加上计算结束。
从这个例子就能看出,一个装饰器能完成很多之前可能需要很多次手动重复的工作,此处装饰器可以用在很多的计算过程中,能够起到打印计算开始和计算结束的效果,因此装饰器在某些工作中还是很有用处的。
使用@的装饰器
其实python并不需要一直像上面一样每次都赋值一次(即ad = decorater()的过程),而是有语法糖形式即@符号,@符号能直接开启函数装饰器的功能,加到某个函数之前就装饰了某个函数。
上面的代码改造后的样子:
def decorater(func):def wrapper(*args,**kargs): # 可以传入的任意参数print("计算开始") print("计算结果为:",func(*args,**kargs)) # 调用传入的函数,并打印结果print("计算结束")return wrapper # 返回内函数@decorater
def add(a,b): # 加法return a+b@decorater
def sub(a,b): # 减法return a - b@decorater
def multiply(a,b): # 乘法return a**b@decorater
def divide(a,b): # 除法return a/badd(4,5)
sub(4,5)
multiply(4,5)
divide(4,5)
执行后会发现结果与上面的运行结果一样,这样是不是就十分方便啦!
打印要执行的函数名时出错(functools.wraps的使用)
执行下面的代码,结果得到的add的函数名为wrapper。这是因为python内部是将add函数的内容都传递给了内函数即wrapper,此时执行相当于在执行与add功能相同的wrapper函数。
def decorater(func):def wrapper(*args,**kargs): # 可以传入的任意参数print("计算开始")# print(func.__name__)print("计算结果为:",func(*args,**kargs)) # 调用传入的函数,并打印结果print("计算结束")return wrapper # 返回内函数@decorater
def add(a,b): # 加法return a+bprint(add.__name__)
显然这不是我们想要的。但functools中有个wraps模块能帮我们解决这个问题。
from functools import wraps
def decorater(func):@wraps(func)def wrapper(*args,**kargs): # 可以传入的任意参数print("计算开始")# print(func.__name__)print("计算结果为:",func(*args,**kargs)) # 调用传入的函数,并打印结果print("计算结束")return wrapper # 返回内函数@decorater
def add(a,b): # 加法return a+bprint(add.__name__)
这样执行结果就变为了add。
可以传入参数的装饰器
若是装饰器本身需要传入参数,那么就会稍微复杂一点,需要三层嵌套,但是使用起来也是一样。下面看例子:
def outside(text):def decorater(func):def wrapper(*args,**kargs): # 可以传入的任意参数print("计算开始")# print(func.__name__)print("计算结果为:",func(*args,**kargs),text) # 调用传入的函数,并打印结果print("计算结束")return wrapper # 返回内函数return decorater@outside('看得到计算结果不?')
def add(a,b): # 加法return a+badd(4,5)
运行结果:
叮!
实际上可以看出装饰器就是在闭包的基础上来的,因此我觉得最好是理解一下闭包的概念啦,我之前就有写过,哈哈。从这里穿越
参考:Python 函数装饰器
参考:装饰器
参考:https://www.zhihu.com/question/26930016/answer/1047233982