python函数装饰器(def上的@)——Python学习系列

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')
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值