A Python library that has existed for 10 years but is seriously underestimated

Posted by phpIsKillingMe on Thu, 03 Mar 2022 16:57:13 +0100

This article has been included in the Python black magic manual v2 Version 1, online documents, please go to

Python dark magic manual 2.0 document

Today we introduce a library decorator that has existed for ten years but is still not popular. It seems that few people know its existence.

What can this library do for you?

In fact, it is very simple. It can help you write Python decorator code more conveniently. More importantly, it makes the method decorated by the decorator in Python look more like the method before decoration.

This article will not introduce you to the basic knowledge of decorators too much. I will default that you know what decorators are and how to write a simple decorator.

If you don't know about decorators, you can read the article I wrote before and introduce various implementation methods of decorators in a very comprehensive and detailed way.

Conventional decorator

This is the simplest example of a decorator. A log will be printed before and after running the myfunc function.

def deco(func):
    def wrapper(*args, **kw):
        print("Ready to run task")
        func(*args, **kw)
        print("Successful to run task")
    return wrapper

@deco
def myfunc():
    print("Running the task")

myfunc()

Decorators seem to be high-end and magical when used. For some repetitive functions, we often encapsulate them into a decorator function.

When defining a decorator, we all need to write a nested function mechanically as above. Beginners who do not understand the principle of decorator often forget how to define decorator after a period of time.

Some smart students will use PyCharm to automatically generate decorator templates

Then when you want to use it, directly typing deco will generate a simple generator code to improve the preparation efficiency of coding

Use God Library

Using PyCharm's Live Template can reduce the difficulty of writing decorators, but it depends on PyCharm, a professional code editor.

Here, Mingge wants to teach you a simpler method. To use this method, you need to install a library: decorator. You can easily install it with pip

$ python3 -m pip install decorator

From the name of the library, it is not difficult to see that this is a third-party library specially used to solve the problem of decorators.

After having it, you will be surprised to find that the decorator defined by yourself will no longer need to write nested functions

from decorator import decorator

@decorator
def deco(func, *args, **kw):
    print("Ready to run task")
    func(*args, **kw)
    print("Successful to run task")

@deco
def myfunc():
    print("Running the task")

myfunc()

deco is a decorative function. The first parameter is fixed, which refers to the decorated function, while the following parameters are fixed. The writing method of variable parameters * args and * * kw is used, and the code is the original parameter of the decorated function.

This kind of code is easier to understand and more logical.

Decorator with parameters

Decorators can be divided into two types according to whether they carry parameters

The first is the simplest example without parameters, which has been illustrated above

def decorator(func):
    def wrapper(*args, **kw):
        func(*args, **kw)
    return wrapper

The second kind: with parameters, which is relatively complex and not so easy to understand.

def decorator(arg1, arg2):
    def wrapper(func):
        def deco(*args, **kwargs)
            func(*args, **kwargs)
        return deco
    return wrapper

Can decorator also support decorators with parameters?

Here is an official example

from decorator import decorator

@decorator
def warn_slow(func, timelimit=60, *args, **kw):
    t0 = time.time()
    result = func(*args, **kw)
    dt = time.time() - t0
    if dt > timelimit:
        logging.warn('%s took %d seconds', func.__name__, dt)
    else:
        logging.info('%s took %d seconds', func.__name__, dt)
    return result
  
@warn_slow(timelimit=600)  # warn if it takes more than 10 minutes
def run_calculation(tempdir, outdir):
    pass

Can see

  • The first parameter of the decorator function is the decorator func, which is the same as before
  • The second parameter, timelimit, is written as a location parameter and has a default value
  • In the future, the writing method of variable parameters is used as before

It is not difficult to infer that as long as you start with the second parameter in the decorator function and use the writing method of non variable parameters, these parameters can be used as the parameters of the decorator call.

Has the signature problem been solved?

When we write our own decorators, we usually add one called functools Wraps decorator, I think you should have seen it often. What's its use?

Let's start with an example

def wrapper(func):
    def inner_function():
        pass
    return inner_function

@wrapper
def wrapped():
    pass

print(wrapped.__name__)
#inner_function

Why is it like this? Shouldn't func be returned?

This is not difficult to understand, because the upper implementation of func and the lower decorator(func) are equivalent, so the above func__ name__ Is equivalent to the following decorator(func)__ name__ Yes, of course, the name is inner_function

def wrapper(func):
    def inner_function():
        pass
    return inner_function

def wrapped():
    pass

print(wrapper(wrapped).__name__)
#inner_function

At present, we can see that when a function is decorated by a decorator, its signature information will change (such as the function name seen above)

How to avoid this situation?

The solution is to use the functools we mentioned earlier Wraps decorator.

Its function is to assign some attribute values of the wrapped function to the wrapper function, and finally make the display of attributes more in line with our intuition.

from functools import wraps

def wrapper(func):
    @wraps(func)
    def inner_function():
        pass
    return inner_function

@wrapper
def wrapped():
    pass

print(wrapped.__name__)
# wrapped

So the question is, will there still be such a signature problem after we use decorator?

Write an example to verify it

from decorator import decorator

@decorator
def deco(func, *args, **kw):
    print("Ready to run task")
    func(*args, **kw)
    print("Successful to run task")

@deco
def myfunc():
    print("Running the task")

print(myfunc.__name__)

The output is myfunc, indicating that the decorator has helped us deal with all foreseeable problems by default.

To sum up

Decorator is a third-party library to improve the coding efficiency of decorators. It is suitable for novices who are confused about the principle of decorators. It can make it easy for you to write code that is more in line with human intuition. The definition of decorator with parameters is very complex. It needs to write multi-layer nested functions, and you need to be familiar with the transfer path of each parameter to ensure that the decorator you write can be used normally. At this time, as long as you use the decorator library, you can easily write a decorator with parameters. At the same time, you don't have to worry about his signature problem. It's all handled properly for you.

Such a great library, I recommend you to use it.

The python black magic manual is a summary of my many years of Python experience. This manual is an e-book dedicated to Python black magic playing methods. These include some rare Python knowledge, various playing methods of Python Shell, various crazy Python technology operations, super detailed advanced knowledge interpretation of python, and constantly expanding and improving the development skills suitable for all Python developers.

This book aims to introduce you to a different Python, not a basic teaching material for beginners.

If you are interested in Python development skills and black magic, don't miss this manual. The reading links of online documents are as follows. Welcome to learn:

Python black magic Manual: http://magic.iswbm.com

All the articles published by me are original and cost me a lot of effort. If the articles help you, please help to have a quality third company.

Topics: Python