Python函数装饰器
python
中偶尔会看到以下形式的代码:
@functionA
def functionB():
...
return
那么@functionA
的作用是什么?先上答案:
@functionA <=> functionB = functionA(functionB)
下面通过案例进行解释,有不足之处欢迎大佬们指正。
假设Tom
需要买车,那么我们可以写出最简单的函数:
def buy_car():
logging.info('You are trying to buy a car!')
buy_car()
#运行结果如下:
[2021-07-14 15:17:41,377 - 9944]: You are trying to buy a car!
Tom
看后增加了要求,想要按照汽车的类型进行购买,显然我们需要传入一个有关类型的参数,于是便有了下面的函数:
def buy_car_by_type(car_type):
logging.info('You are buying a car by type: {}'.format(car_type))
buy_car_by_type('Motorcycle')
#运行结果如下:
[2021-07-14 15:17:41,378 - 9944]: You are buying a car by type: Motorcycle
Tom
继续增加要求,想要按照汽车的名称进行购买,那么我们需要传入一个有关名称的参数,于是便有了下面的函数:
def buy_car_by_name(car_name):
logging.info('You are buying a car by name: {}'.format(car_name))
buy_car_by_name('Dinka Akuma')
#运行结果如下:
[2021-07-14 15:17:41,377 - 9944]: You are buying a car by name: Dinka Akuma
让我们尝试把两个函数合成一个,看来需要一个参数决定依据名称还是类型进行购买:
def buy_car_by(option, args):
logging.info('You are trying to buy a car!')
if option == 'car_type':
logging.info(' --[ You are buying a car by type: {}'.format(args))
elif option == 'car_name':
logging.info(' --[ You are buying a car by name: {}'.format(args))
buy_car_by('car_name', 'Gallivanter Baller')
buy_car_by('car_type', 'Sports car')
#运行结果如下:
[2021-07-14 15:44:00,056 - 12412]: You are trying to buy a car!
[2021-07-14 15:44:00,056 - 12412]: --[ You are buying a car by name: Gallivanter Baller
[2021-07-14 15:44:00,056 - 12412]: You are trying to buy a car!
[2021-07-14 15:44:00,056 - 12412]: --[ You are buying a car by type: Sports car
好像有点麻烦,每次都需要输入两个参数,既然Python
语言万物皆对象,让我们改进一下,在函数外层实现类型或者名称的输入,然后在内部函数进行选择并返回该函数。
def buy_car_by2(option):
def choose(args):
logging.info('You are trying to buy a car!')
if option == 'car_type':
logging.info(' --[ You are buying a car by type: {}'.format(args))
elif option == 'car_name':
logging.info(' --[ You are buying a car by name: {}'.format(args))
else:
logging.info(' --[ You wanna buy a car in a new way? Get out!')
# 将内部函数直接返回
return choose
a = buy_car_by2('car_name')
# 此处的a就等价于
def a('car_name'):
logging.info(' --[ You are buying a car by name: {}'.format(args))
b = buy_car_by2('car_type')
c = buy_car_by2('color')
a('Gallivanter Baller')
b('SUV')
c('red color')
#运行结果如下:
[2021-07-14 16:04:18,832 - 6716]: You are trying to buy a car!
[2021-07-14 16:04:18,832 - 6716]: --[ You are buying a car by name: Gallivanter Baller
[2021-07-14 16:04:18,832 - 6716]: You are trying to buy a car!
[2021-07-14 16:04:18,833 - 6716]: --[ You are buying a car by type: SUV
[2021-07-14 16:04:18,833 - 6716]: You are trying to buy a car!
[2021-07-14 16:04:18,833 - 6716]: --[ You wanna buy a car in a new way? Get out!
如果Tom
再次改变了需求呢?比如想增加一个按照颜色买车的模块。buy_car_by2
中既然可以返回一个函数,那么也可以传入函数。于是我们便有了:
def buy_cars(func):
def choose(args):
logging.info('You are trying to buy a car!')
if 'type' in func.__name__:
logging.info(' --[ You are buying a car by type: {}'.format(args))
elif 'name' in func.__name__:
logging.info(' --[ You are buying a car by name: {}'.format(args))
else:
logging.info(' --[ You are buying a car by else: {}'.format(args))
func(args)
return choose
@buy_cars
def buy_car_by_color(args):
logging.info(' --[ Color detail: {}'.format(args))
@buy_cars
def buy_car_by_type(args):
logging.info(' --[ Type detail: {}'.format(args))
buy_car_by_color('Red')
buy_car_by_type('SUV')
#实验结果:
[2021-07-14 17:18:26,986 - 3728]: You are trying to buy a car!
[2021-07-14 17:18:26,986 - 3728]: --[ You are buying a car by else: Red
[2021-07-14 17:18:26,986 - 3728]: --[ Color detail: Red
[2021-07-14 17:18:26,986 - 3728]: You are trying to buy a car!
[2021-07-14 17:18:26,986 - 3728]: --[ You are buying a car by type: SUV
[2021-07-14 17:18:26,986 - 3728]: --[ Type detail: SUV
至此,我们已经进行了函数装饰器的使用。我们来把后面两个函数分析一下,看看它们到底做了什么。
- 首先
buy_car_by_color
需要一个参数args
,指示需求是什么颜色。
def buy_car_by_color(args):
logging.info(' --[ Color detail: {}'.format(args))
- 随后执行装饰器,将函数
buy_car_by_color
送入buy_cars()
,此时的buy_cars()
变成了:
def buy_cars(buy_car_by_color):
def choose(args):
logging.info('You are trying to buy a car!')
if 'type' in func.__name__:
logging.info(' --[ You are buying a car by type: {}'.format(args))
elif 'name' in func.__name__:
logging.info(' --[ You are buying a car by name: {}'.format(args))
else:
# 传入buy_car_by_color时执行该语句
logging.info(' --[ You are buying a car by name: {}'.format(args))
# buy_car_by_color
buy_car_by_color()
return choose
- 执行完毕后将内部函数
choose
返回给buy_car_by_color
buy_car_by_color = buy_cars(buy_car_by_color)
# 此时的buy_car_by_color等价于
def choose(args):
logging.info('You are trying to buy a car!')
logging.info(' --[ You are buying a car by else: {}'.format(args))
logging.info(' --[ Color detail: {}'.format(args))
至此,装饰器就完成了对所装饰函数的重构。
附上案例中所使用的代码源码:
import logging
logging.basicConfig(level=logging.INFO)
def buy_car():
logging.info('You are trying to buy a car!')
# 增加需求,按照类型获取
def buy_car_by_type(car_type):
logging.info('You are buying a car by type: {}'.format(car_type))
# 增加需求,按照名称获取
def buy_car_by_name(car_name):
logging.info('You are buying a car by name: {}'.format(car_name))
def buy_car_by(option, args):
logging.info('You are trying to buy a car!')
if option == 'car_type':
logging.info(' --[ You are buying a car by type: {}'.format(args))
elif option == 'car_name':
logging.info(' --[ You are buying a car by name: {}'.format(args))
def buy_car_by2(option):
option_list = ['car_type', 'car_name']
def choose(args):
logging.info('You are trying to buy a car!')
logging.info('Here is the options: {}'.format(option_list))
if option == 'car_type':
logging.info(' --[ You are buying a car by type: {}'.format(args))
elif option == 'car_name':
logging.info(' --[ You are buying a car by name: {}'.format(args))
else:
logging.info(' --[ You wanna buy a car in a new way? Get out!')
return choose
def buy_cars(func):
def choose(args):
logging.info('You are trying to buy a car!')
if 'type' in func.__name__:
logging.info(' --[ You are buying a car by type: {}'.format(args))
elif 'name' in func.__name__:
logging.info(' --[ You are buying a car by name: {}'.format(args))
else:
logging.info(' --[ You are buying a car by else: {}'.format(args))
func(args)
return choose
@buy_cars
def buy_car_by_color(args):
logging.info(' --[ Color detail: {}'.format(args))
@buy_cars
def buy_car_by_type(args):
logging.info(' --[ Type detail: {}'.format(args))
buy_car()
buy_car_by_name('Dinka Akuma')
buy_car_by_type('Motorcycle')
buy_car_by('car_name', 'Gallivanter Baller')
buy_car_by('car_name', 'Benefactor Schafter')
buy_car_by('car_type', 'Sports car')
a = buy_car_by2('car_name')
b = buy_car_by2('car_type')
c = buy_car_by2('color')
a('Gallivanter Baller')
b('SUV')
c('red color')
buy_car_by_color('Red')
buy_car_by_type('SUV')