[PYTHON] How to use the decorator

I understand how to write a decorator.

A decorator, as the name implies, decorates. What you decorate is already a function, a function. Another method.

I understand. It's hard. I understand the decorator.

Takeshi muttered and started running as if he had been flipped. But the signal started blinking and stopped. Yes, he follows the traffic rules.

And think about leaving time.

Humanity is what I want to use when I can write. However, when I actually want to use it, I don't know where to use it. I can't think of an effective use.

Such a person.

Yes! I don't even know!

No, but maybe I've come to understand a little.

Why use decorators

Why use a decorator?

-Use decorators for unwanted redundancy elimination and DRY

--Use decorators to improve readability

I can think of something like this. This is often mentioned as an advantage of using it.

This is nothing wrong! Overwhelmingly correct! Oh, I'm sure it's correct!

But it's just that I don't know where to use it.

Because, for the reasons mentioned above, there are many other ways to do it, right? For example, is there inheritance? Isn't it okay to just use it as a function? You're used to it, right? I don't want to bother to write something like a decorator. I think so, but are you complaining?

Do you think? Don't you think I think.

So why dare to use a decorator?

To think about it, we need to pay attention to the ** meaning ** of the processing of the code. The bottom line here is that we use decorators for ** separation of business logic **.

Separation of business logic

Decorator to trace function calls

You've probably seen an example like this as the first step in decorating.

from functools import wraps

def trace_func_call(func):
    @wraps(func)
    def wrapper(*arg):
        print(func.__name__ + ' is called.')
        return func(*arg)
    return wrapper

@trace_func_call
def spam(s):
    print((s + '!') * 5)

@trace_func_call
def ham(s):
    print((s + '?') * 5)

spam('egg')
ham('sausage')

Execution result

spam is called.
egg!egg!egg!egg!egg!
ham is called.
sausage?sausage?sausage?sausage?sausage?

A decorator that displays it each time a function is called.

At this time, the role that the decorator plays is not business logic, isn't it? The example is super-appropriate, but the processing you want to do as business logic is the processing of the spam and ham functions, isn't it? It's a crappy guy who repeats 5 times with ! Or ? As an argument.

At that time, displaying the function call is not the business logic, but the logic to implement it because it is necessary for management. Their logic is semantically different.

You can use decorators to separate management logic from business logic. In other words, based on what you want to do, decorators are the ones that realize the intention of separating the business logic, which is the main purpose of the code, from the management logic in a neat way. Of course, I don't mean to say that this is the only role of the decorator, but it gives us great suggestions on how to use the decorator.

Cache decorator

Next is a little more practical. This is a sample decorator from Raymond Hettinger's talk ** "Transforming Code into Beautiful, Idiomatic Python" ** at PyCon US 2013.

Let's start with the original "less desirable" code.

def web_lookup(url, saved={}):
    if url in saved:
        return saved[url]
    page = urllib.urlopen(url).read()
    saved[url] = page
    return page

Click here for a better version of the above code using a decorator.

def cache(func):
    saved = {}
    @wraps(func)
    def newfunc(*args):
        if args in saved:
            return saved[args]
        result = func(*args)
        saved[args] = result
        return result
    return newfunc

@cache
def web_lookup(url):
    return urllib.urlopen(url).read()

The web_lookup function is a function that gets the web page of the specified URL. At that time, it has a function to cache the acquired pages.

It's a very short code, but you can see that the functionality of this code is semantically divided as follows:

--Business logic -> Get the web page of the specified URL --Administrative logic -> Get the web page of the specified URL

In the original code, the caching functionality is provided by a dictionary of arguments passed to the web_lookup function. In other words, the function itself is an implementation that incorporates the functionality of business logic and administrative logic.

On the other hand, in better code, the function implements only the business logic, and the administrative logic is implemented by the closed decorator. You'll find that doing this clearly separates the business logic and improves the readability and maintainability of the code.

The amount of code has increased with the implementation of decorators, but I feel that calling it "beautiful code" is a very pythonic feeling.

By the way, these cache decorator patterns are standard patterns, so it's not difficult and you should definitely try it.

By the way, I think the code of the quoted lecture slides is wrong ... So I am modifying the code I wrote above. This is the decorator. You're wrong, right? https://speakerdeck.com/pyconslides/transforming-code-into-beautiful-idiomatic-python-by-raymond-hettinger-1

Another simple example

Then another one.

Especially when using 2.x and the character code of the input is unknown, I'm really tired of decoding somehow by handling the character string, somehow unicode, etc., so in the internal processing I use chardet with a decorator Let's make everything unicode and process it.

It may be useful when writing a little tool.

import chardet

def unicode_conv(func):
    def wrapper(*args):
        f = lambda x: x.decode(chardet.detect(x)['encoding'])
        args = [f(arg) if type(arg) != unicode else arg for arg in args]
        return func(*args)
    return wrapper

def funcA(*args):
    for arg in args:
        print(repr(arg))

@unicode_conv
def funcB(*args):
    for arg in args:
        print(repr(arg))

funcA('shrubbery')
funcB('shrubbery')
funcB(u'shrubbery')

Execution result

'shrubbery'
u'shrubbery'
u'shrubbery'

Recommended Posts

How to use the decorator
How to use the zip function
How to use the optparse module
How to use the ConfigParser module
How to use xml.etree.ElementTree
How to use Python-shell
How to use tf.data
How to use virtualenv
How to use the Spark ML pipeline
How to use Seaboan
How to use image-match
How to use Pandas 2
How to use Virtualenv
How to use pytest_report_header
[Linux] How to use the echo command
How to use Bio.Phylo
How to use SymPy
How to use x-means
How to use WikiExtractor.py
How to use IPython
How to use virtualenv
How to use Matplotlib
How to use iptables
How to use numpy
How to use TokyoTechFes2015
How to use venv
How to use dictionary {}
How to use Pyenv
How to use python-kabusapi
How to use the IPython debugger (ipdb)
How to use return
How to use dotenv
How to use pyenv-virtualenv
How to use Go.mod
How to use imutils
How to use import
How to use the C library in Python
How to use MkDocs for the first time
How to use the graph drawing library Bokeh
How to use the Google Cloud Translation API
How to use the NHK program guide API
[Algorithm x Python] How to use the list
How to use Qt Designer
How to use search sorted
[gensim] How to use Doc2Vec
python3: How to use bottle (2)
Understand how to use django-filter
[Python] How to use list 1
How to use FastAPI ③ OpenAPI
How to use IPython Notebook
How to use Pandas Rolling
[Note] How to use virtualenv
Python: How to use pydub
[Python] How to use checkio
How to use Django's GeoIp2
[Python] How to use input ()
[Introduction] How to use open3d
How to use Python lambda
How to use Jupyter Notebook
[Python] How to use virtualenv
python3: How to use bottle (3)