Table of Contents
Simple Decorator
@ Syntactic sugar
*args, **kwargs
带参数的装饰器
类装饰器
functools.wraps
装饰器顺序
Home Backend Development Python Tutorial A detailed introduction to must-see Python decorators

A detailed introduction to must-see Python decorators

Mar 17, 2017 pm 05:31 PM

Before talking about Python decorators, I would like to give an example first. Although it is a bit dirty, it is very relevant to the topic of decorators.

The main function of the underwear that everyone has is to cover our shame, but in winter it cannot protect us from wind and cold. What should we do? One way we thought of was to transform the underwear to make it thicker and longer. In this way, it not only has the function of covering shame, but also provides warmth. However, there is a problem. After we transformed the underwear into trousers, , although it still has a shame-covering function, it is essentially no longer a real pair of underwear. So smart people invented trousers and put the trousers directly over the underwear without affecting the underwear. In this way, the underwear is still underwear. With the trousers, the baby will no longer be cold. Decorators are like the trousers we are talking about here. They provide warmth to our bodies without affecting the function of underwear.

Before talking about decorators, you need to understand one thing first. Functions in Python are different from Java and C++. Functions in Python can be passed to another function as parameters like ordinary variables, for example :

def foo():
    print("foo")

def bar(func):
    func()

bar(foo)
Copy after login

Officially back to our topic. A decorator is essentially a Python function or class that allows other functions or classes to add additional functionality without making any code modifications. The return value of the decorator is also a function/class object. It is often used in scenarios with cross-cutting requirements, such as: log insertion, performance testing, transaction processing, caching, permission verification, etc. Decorators are an excellent design to solve such problems. With decorators, we can extract a large amount of similar code that has nothing to do with the function itself into decorators and continue to reuse it. In a nutshell, the purpose of a decorator is to add additional functionality to an existing object.

Let’s look at a simple example first, although the actual code may be much more complicated than this:

def foo():
    print('i am foo')
Copy after login

Now there is a new requirement. I hope to record the execution log of the function, so I add it to the code Log code:

def foo():
    print('i am foo')
    logging.info("foo is running")
Copy after login

If the functions bar() and bar2() have similar requirements, what should be done? Write another logging in the bar function? This results in a lot of similar code. In order to reduce repeated code writing, we can do this and redefine a new function: specifically to process logs, and then execute the real business code after the logs are processed

def use_logging(func):
    logging.warn("%s is running" % func.__name__)
    func()

def foo():
    print('i am foo')

use_logging(foo)
Copy after login

Do this logic There is no problem, the function is implemented, but when we call it, we no longer call the real business logic foo function, but replace it with the use_logging function, which destroys the original code structure. Now we have to call it every time Every time I have to pass the original foo function as a parameter to the use_logging function, is there a better way? Of course there is, the answer is decorators.

Simple Decorator

def use_logging(func):

def wrapper():
        logging.warn("%s is running" % func.__name__)
return func()   # 把 foo 当做参数传递进来时,执行func()就相当于执行foo()
return wrapper

def foo():
    print('i am foo')

foo = use_logging(foo)  # 因为装饰器 use_logging(foo) 返回的时函数对象 wrapper,这条语句相当于  foo = wrapper
foo()                   # 执行foo()就相当于执行 wrapper()
Copy after login

use_logging is a decorator, it is an ordinary function, it wraps the function func that executes the real business logic in it, it looks like foo is decorated with use_logging , use_logging also returns a function, and the name of this function is wrapper. In this example, when the function enters and exits, it is called a cross-section. This programming method is called aspect-oriented programming.

@ Syntactic sugar

If you have been in contact with Python for a while, you must be familiar with the @ symbol. Yes, the @ symbol is the syntactic sugar of the decorator. It is placed in the function The place where the definition begins, so that the last step of reassignment can be omitted.

def use_logging(func):

def wrapper():
        logging.warn("%s is running" % func.__name__)
return func()
return wrapper

@use_logging
def foo():
    print("i am foo")

foo()
Copy after login

As shown above, with @ , we can omit the sentence foo = use_logging(foo), and directly call foo() to get the desired result. Did you see that the foo() function does not need to be modified in any way, just add a decorator where it is defined, and the call is still the same as before. If we have other similar functions, we can continue to call the decorator to decorate it. function without having to repeatedly modify the function or add new packages. In this way, we improve the reusability of the program and increase the readability of the program.

The reason why decorators are so convenient to use in Python is that Python functions can be passed as parameters to other functions like ordinary objects, can be assigned to other variables, can be used as return values, and can be defined. within another function.

*args, **kwargs

Someone may ask, what if my business logic function foo requires parameters? For example:

def foo(name):
    print("i am %s" % name)
Copy after login

We can specify the parameters when defining the wrapper function:

def wrapper(name):
        logging.warn("%s is running" % func.__name__)
return func(name)
return wrapper
Copy after login

In this way, the parameters defined by the foo function can be defined in the wrapper function. At this time, someone wants to ask again, what if the foo function receives two parameters? What about the three parameters? What's more, I may pass many of them. When the decorator doesn't know how many parameters foo has, we can use *args instead:

def wrapper(*args):
        logging.warn("%s is running" % func.__name__)
return func(*args)
return wrapper
Copy after login

In this way, no matter how many parameters foo defines, I can pass them completely to func. go. This will not affect foo's business logic. At this time, some readers may ask, what if the foo function also defines some keyword parameters? For example:

def foo(name, age=None, height=None):
    print("I am %s, age %s, height %s" % (name, age, height))
Copy after login

At this time, you can specify the keyword function for the wrapper function:

def wrapper(*args, **kwargs):
# args是一个数组,kwargs一个字典
        logging.warn("%s is running" % func.__name__)
return func(*args, **kwargs)
return wrapper
Copy after login

带参数的装饰器

装饰器还有更大的灵活性,例如带参数的装饰器,在上面的装饰器调用中,该装饰器接收唯一的参数就是执行业务的函数 foo 。装饰器的语法允许我们在调用时,提供其它参数,比如@decorator(a)。这样,就为装饰器的编写和使用提供了更大的灵活性。比如,我们可以在装饰器中指定日志的等级,因为不同业务函数可能需要的日志级别是不一样的。

def use_logging(level):
def decorator(func):
def wrapper(*args, **kwargs):
if level == "warn":
                logging.warn("%s is running" % func.__name__)
elif level == "info":
                logging.info("%s is running" % func.__name__)
return func(*args)
return wrapper

return decorator

@use_logging(level="warn")
def foo(name='foo'):
    print("i am %s" % name)

foo()
Copy after login

上面的 use_logging 是允许带参数的装饰器。它实际上是对原有装饰器的一个函数封装,并返回一个装饰器。我们可以将它理解为一个含有参数的闭包。当我 们使用@use_logging(level=”warn”)调用的时候,Python 能够发现这一层的封装,并把参数传递到装饰器的环境中。

@use_logging(level=”warn”)等价于@decorator

类装饰器

没错,装饰器不仅可以是函数,还可以是类,相比函数装饰器,类装饰器具有灵活度大、高内聚、封装性等优点。使用类装饰器主要依靠类的__call__方法,当使用 @ 形式将装饰器附加到函数上时,就会调用此方法。

class Foo(object):
def __init__(self, func):
        self._func = func

def __call__(self):
print ('class decorator runing')
        self._func()
print ('class decorator ending')

@Foo
def bar():
print ('bar')

bar()
Copy after login

functools.wraps

使用装饰器极大地复用了代码,但是他有一个缺点就是原函数的元信息不见了,比如函数的docstring、__name__、参数列表,先看例子:

# 装饰器
def logged(func):
def with_logging(*args, **kwargs):
print func.__name__      # 输出 'with_logging'
print func.__doc__       # 输出 None
return func(*args, **kwargs)
return with_logging

# 函数
@logged
def f(x):
"""does some math"""
return x + x * x

logged(f)
Copy after login

不难发现,函数 f 被with_logging取代了,当然它的docstring,__name__就是变成了with_logging函数的信息了。好在我们有functools.wraps,wraps本身也是一个装饰器,它能把原函数的元信息拷贝到装饰器里面的 func 函数中,这使得装饰器里面的 func 函数也有和原函数 foo 一样的元信息了。

from functools import wraps
def logged(func):
    @wraps(func)
def with_logging(*args, **kwargs):
print func.__name__      # 输出 'f'
print func.__doc__       # 输出 'does some math'
return func(*args, **kwargs)
return with_logging

@logged
def f(x):
"""does some math"""
return x + x * x
Copy after login

装饰器顺序

一个函数还可以同时定义多个装饰器,比如:

@a
@b
@c
def f ():
    pass
Copy after login

它的执行顺序是从里到外,最先调用最里层的装饰器,最后调用最外层的装饰器,它等效于

f = a(b(c(f)
Copy after login



The above is the detailed content of A detailed introduction to must-see Python decorators. For more information, please follow other related articles on the PHP Chinese website!

Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn

Hot AI Tools

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undress AI Tool

Undress AI Tool

Undress images for free

Clothoff.io

Clothoff.io

AI clothes remover

AI Hentai Generator

AI Hentai Generator

Generate AI Hentai for free.

Hot Article

R.E.P.O. Energy Crystals Explained and What They Do (Yellow Crystal)
4 weeks ago By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Best Graphic Settings
4 weeks ago By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. How to Fix Audio if You Can't Hear Anyone
4 weeks ago By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Chat Commands and How to Use Them
4 weeks ago By 尊渡假赌尊渡假赌尊渡假赌

Hot Tools

Notepad++7.3.1

Notepad++7.3.1

Easy-to-use and free code editor

SublimeText3 Chinese version

SublimeText3 Chinese version

Chinese version, very easy to use

Zend Studio 13.0.1

Zend Studio 13.0.1

Powerful PHP integrated development environment

Dreamweaver CS6

Dreamweaver CS6

Visual web development tools

SublimeText3 Mac version

SublimeText3 Mac version

God-level code editing software (SublimeText3)

How to solve the permissions problem encountered when viewing Python version in Linux terminal? How to solve the permissions problem encountered when viewing Python version in Linux terminal? Apr 01, 2025 pm 05:09 PM

Solution to permission issues when viewing Python version in Linux terminal When you try to view Python version in Linux terminal, enter python...

How to teach computer novice programming basics in project and problem-driven methods within 10 hours? How to teach computer novice programming basics in project and problem-driven methods within 10 hours? Apr 02, 2025 am 07:18 AM

How to teach computer novice programming basics within 10 hours? If you only have 10 hours to teach computer novice some programming knowledge, what would you choose to teach...

How to efficiently copy the entire column of one DataFrame into another DataFrame with different structures in Python? How to efficiently copy the entire column of one DataFrame into another DataFrame with different structures in Python? Apr 01, 2025 pm 11:15 PM

When using Python's pandas library, how to copy whole columns between two DataFrames with different structures is a common problem. Suppose we have two Dats...

How to avoid being detected by the browser when using Fiddler Everywhere for man-in-the-middle reading? How to avoid being detected by the browser when using Fiddler Everywhere for man-in-the-middle reading? Apr 02, 2025 am 07:15 AM

How to avoid being detected when using FiddlerEverywhere for man-in-the-middle readings When you use FiddlerEverywhere...

What are regular expressions? What are regular expressions? Mar 20, 2025 pm 06:25 PM

Regular expressions are powerful tools for pattern matching and text manipulation in programming, enhancing efficiency in text processing across various applications.

How does Uvicorn continuously listen for HTTP requests without serving_forever()? How does Uvicorn continuously listen for HTTP requests without serving_forever()? Apr 01, 2025 pm 10:51 PM

How does Uvicorn continuously listen for HTTP requests? Uvicorn is a lightweight web server based on ASGI. One of its core functions is to listen for HTTP requests and proceed...

How to dynamically create an object through a string and call its methods in Python? How to dynamically create an object through a string and call its methods in Python? Apr 01, 2025 pm 11:18 PM

In Python, how to dynamically create an object through a string and call its methods? This is a common programming requirement, especially if it needs to be configured or run...

What are some popular Python libraries and their uses? What are some popular Python libraries and their uses? Mar 21, 2025 pm 06:46 PM

The article discusses popular Python libraries like NumPy, Pandas, Matplotlib, Scikit-learn, TensorFlow, Django, Flask, and Requests, detailing their uses in scientific computing, data analysis, visualization, machine learning, web development, and H

See all articles