Python function decorator (on def @) -- Python Learning Series

Posted by hawkeyes on Mon, 17 Jan 2022 00:25:34 +0100

Python function decorator

Occasionally, you will see the following forms of code in python:

@functionA
def functionB():
	...
	return

So what is the role of @ functionA? First answer:

@functionA <=> functionB = functionA(functionB)

The following is an explanation through a case. If there are deficiencies, you are welcome to correct them.

Assuming Tom needs to buy a car, we can write the simplest function:

def buy_car():
    logging.info('You are trying to buy a car!')

buy_car()

#The operation results are as follows:
[2021-07-14 15:17:41,377 - 9944]: You are trying to buy a car!

Tom added a requirement after reading it. He wanted to buy according to the type of car. Obviously, we need to pass in a parameter related to the type, so he has the following function:

def buy_car_by_type(car_type):
    logging.info('You are buying a car by type: {}'.format(car_type))

buy_car_by_type('Motorcycle')

#The operation results are as follows:
[2021-07-14 15:17:41,378 - 9944]: You are buying a car by type: Motorcycle

Tom continues to add requirements. If he wants to purchase according to the name of the car, we need to pass in a parameter about the name, so he has the following function:

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')

#The operation results are as follows:
[2021-07-14 15:17:41,377 - 9944]: You are buying a car by name: Dinka Akuma

Let's try to combine the two functions into one. It seems that we need a parameter to decide whether to buy by name or type:

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')

#The operation results are as follows:
[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

It seems a little troublesome. You need to input two parameters every time. Since everything in Python language is an object, let's improve it. Implement the input of type or name in the outer layer of the function, and then select and return the function in the inner function.

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 the inner function directly
    return choose


a = buy_car_by2('car_name')

# a here is equivalent to
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')

#The operation results are as follows:
[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!

What if Tom changes his needs again? For example, I want to add a module to buy a car according to color. buy_ car_ Since you can return a function in BY2, you can also pass in a function. So we have:

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')

#Experimental results:
[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

So far, we have used the function decorator. Let's analyze the last two functions to see what they do.

  • First buy_car_by_color requires a parameter args to indicate what color is required.
def buy_car_by_color(args):
    logging.info('  --[ Color detail: {}'.format(args))
  • The decorator is then executed, placing the function buy_car_by_color buy_cars(), buy at this time_ Cars () becomes:
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:
        	# Incoming buy_car_by_color to execute the statement
            logging.info('  --[ You are buying a car by name: {}'.format(args))
        # buy_car_by_color
        buy_car_by_color()

    return choose
  • After execution, return the internal function choose to buy_car_by_color
buy_car_by_color = buy_cars(buy_car_by_color)
# Buy at this time_ car_ by_ Color is equivalent to
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))

So far, the decorator has completed the reconstruction of the decorated function.

Topics: Python Programming