I can't ask you again (?) Python Knowledge Series -Decorator-

Introduction

Classes, methods, iterators, constructors ... When programming in Python (but not limited to), a lot of jargon and concepts come up. Here, I will explain the terms and usage that I can not hear anymore, including the meaning of flaming learning and organizing my head.

The first is Decorator. (It is undecided whether to do the second time)

What is a decorator?

In a nutshell, it's __ that gives functionality to __functions. Let's take a look at one sample code anyway.

print_args_decorator.py


# -*- coding: utf-8 -*-
#Decorator to display arguments
def print_args(func):
    def wrapper(*args, **kwargs):
        res = func(*args, **kwargs)
        print('<args is ...>')
        for arg in args:
            print(arg)
        for kwarg in kwargs:
            print('{0}:{1}'.format(kwarg, kwargs[kwarg]))
        return res
    return wrapper

Let's decorate the function with this decorator. Prepare the function.

seimei.py


# -*- coding: utf-8 -*-
def seimei(LastName, FirstName):
    """Display arguments as full name"""
    print('Full name:{0} {1}'.format(FirstName, LastName))

if __name__ == '__main__':
    seimei(LastName='Jackson', FirstName='Michael')
Full name:Michael Jackson

We receive the first name and last name and output it as the name. What if you add the decorator you just created to this?

seimei_deco.py


# -*- coding: utf-8 -*-
from print_args_decorator import print_args

@print_args  #Decorator
def seimei(LastName, FirstName):
    """Display arguments as full name"""
    print('Full name:{0} {1}'.format(FirstName, LastName))

if __name__ == '__main__':
    seimei(LastName='Jackson', FirstName='Michael')
Full name:Michael Jackson
<args is ...>
LastName:Jackson
FirstName:Michael

The value of the argument is now output along with the output of the original function. This is because the function that returns the name has a function to output __arguments __. I don't know what it is, but it seems convenient

How to make a decorator

What's happening inside the decorator is explained in detail in the following articles. -12 Steps to Understand Python Decorators

Based on this article and the sample code above, I'll explain the basic operation of the decorator. (Because the above also touches on scope, which is an important concept in programming, it is recommended for Python beginners to read it.)

Receives a function and returns a function

First, make a part that functions as a decorator. In the sample code, it is the hierarchy of ``` def print_args (func):` ``. This function specifies a function as an argument. Also, if you look at the return statement in the same hierarchy, you can see that it returns a function called wrapper. You receive a function, process it (make it a wrapper), and return it.

Define operations to be performed separately from function execution

Next, create a part that defines the function to be given. In the sample code, it is the hierarchy of ``` def wrapper (* args, ** kwargs):` ``. In this function, the func received in the upper layer is executed first. Since I don't want to put restrictions on the arguments of func, I use \ * args, \ * \ * kwargs as arguments. Decorators work for multiple functions and show their true value, so they often take \ * args, \ * \ * kwargs as arguments. After running func, I'm printing the arguments. Note that \ * \ * kwargs stores its arguments as dict, so the arguments are in dict-compliant sort order, not in the order they were created when the function was created.

How to use the decorator

After creating the decorator, let's decorate the function. The decorator is called with an at sign just before the function definition. In the sample code seimei_deco.py, the decorator print_args is called by `` `@ print_args``` just before the semei function. The seimei function now "prints its arguments after the function is executed" in addition to its original behavior.

Let's add a decorator to a function different from the above example. Since it is good that decorators can be added to any function, I think it is better to create them so that they can be used for general purposes, such as specifying \ * args, \ * \ * kwargs as arguments.

sum_deco.py


from print_args_decorator import print_args

@print_args
def sum_print_args(*args):
    return sum(args)

if __name__ == '__main__':
    sum_num = sum_print_args(1, 3)
    print('SUM = {0}'.format(sum_num))
<args is ...>
1
3
SUM = 4

Using functools.wraps

If you use a decorator to generate a function, you will lose information such as document strings. This can be prevented by using functools.wraps when creating the decorator. Let's check the document string of seimei_deeco.py created earlier.

>>> from seimei_deco import seimei
>>> print(seimei.__doc__)
None

The document string will be None. Redefine the decorator using functools.wraps and check the document string of the generated function.

seimei_deco_use_functools.py


# -*- coding: utf-8 -*-
#Decorator to display arguments
def print_args(func):
    import functools
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        res = func(*args, **kwargs)
        print('<args is ...>')
        for arg in args:
            print(arg)
        for kwarg in kwargs:
            print('{0}:{1}'.format(kwarg, kwargs[kwarg]))
        return res
    return wrapper

@print_args  #Decorator
def seimei(LastName, FirstName):
    """Display arguments as full name"""
    print('Full name:{0} {1}'.format(FirstName, LastName))

if __name__ == '__main__':
    seimei(LastName='Jackson', FirstName='Michael')
>>> from seimei_deco_use_functools import seimei
>>> print(seimei.__doc__)
Display arguments as full name

The document string of the original function is displayed. The point here is that functools.wraps itself is used as a decorator. (I referred to this page) As I mentioned earlier, decorators should be created so that they can be used for general purposes as much as possible, so basically functools.wraps should be used.

Where to use

Decorators can be attached to any function. Therefore, it is effective when performing the same operation repeatedly in __ code __. You can do the same thing by creating a function instead of a decorator and calling it one by one in the function definition, but by writing a little at sign one line before the function definition, you can make the __ code readable. Can be raised __. This is very useful when you want to collect error logs for each method in the module.

Also, when I look at the code of the module, I sometimes see a decorator called @property. This makes the function a property, but I won't go into details here. Please refer to this page.

reference

-12 Steps to Understand Python Decorators -Use functools.wraps when writing decorators -About the Python decorator -[About podhmo's diary property] (http://d.hatena.ne.jp/podhmo/20110526/1306422831)

Recommended Posts

I can't ask you again (?) Python Knowledge Series -Decorator-
Fibonacci sequence (I can't ask you again)
I tried Python> decorator
I can't install python3 with pyenv-vertualenv
I can't remember Python regular expressions
I can't install scikit-learn in Python
I can't debug python scripts in Eclipse
I looked back on Python classes again
Why can't I install matplotlib in python! !!
I can't click the Selenium checkbox Python VBA