Decorators in Python

In Python, a decorator is essentially a Python function that allows other functions to add extra functionality without any code changes. The return value of the decorator is also a function object. It is often used in scenarios with aspect requirements, such as: log insertion, performance testing, transaction processing, caching, permission verification and other scenarios. Decorators are an excellent design to solve this kind of problem. With decorators, we can extract a lot of identical code that has nothing to do with the function itself and continue to reuse it. In a nutshell, the role of a decorator is to add extra functionality to an existing function or object .

The following is the simplest decorator that takes no arguments, and its input is a function name.

def debug(func):
    def wrapper(funcarg):
        print("Debug: " + func.__name__ + "...")
        return func(funcarg)
    return wrapper

@debug
def say_hello(name):
    print("hello, " + name + "!")

if __name__ == '__main__':
    say_hello("qinxue")

If you need to pass a lot of parameters, then change the parameters of the wrapper function inside to uniform parameters for any situation.

def debug(func):
    def wrapper(*args, **kwargs):
        print("Debug: " + func.__name__ + "...")
        return func(*args, **kwargs)
    return wrapper

@debug
def say_hello(name, age):
    print("hello, " + name + "!" + "your age is: " + str(age))

if __name__ == '__main__':
    say_hello("qinxue", 24)

Decorators with parameters , at this time, two layers of encapsulation are required. The first layer is a decorator function with parameters, and the second layer is a wrapper function that takes the function name as a parameter.

def logging(level):
    def wrapper(func):
        def inner_wrapper(*args, **kwargs):
            print(level + ":" + "function: " + func.__name__ + "...")
            return func(*args, **kwargs)
        return inner_wrapper
    return wrapper

@logging(level='INFO')
def say(something):
    print("info: " + something)

@logging(level='DEBUG')
def do(something):
    print("debug: " + something)

if __name__ == '__main__':
    say("success!")
    do("opps, something wrong!")

It can be understood that when a decorator with parameters is typed on a function, for example @logging(level='DEBUG'), it is actually a function and will be executed immediately, as long as the result it returns is a decorator, then there is no problem.

class Student():
    def __call__(self):
        print("I am a student!")

s = Student()
s() #I am a student!

This is implemented in a class __call__. Methods with underscores like this are called built-in methods in Python, and sometimes called magic methods. Overriding these magic methods generally changes the internal behavior of the object. The above example allows a class object to have the called behavior.

Going back to the concept of decorators, decorators are required to accept a callable object. It is also possible to use classes to implement it. You can make the constructor of a class __init__()accept a function, then overload __call__()and return a function, or you can achieve the effect of a decorator function.

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

    def __call__(self, *args, **kwargs):
        print("debug: " + self.func.__name__)
        return self.func(*args, **kwargs)

@logging
def greeting(name):
    print("hello, " + name)

if __name__ == '__main__':
    greeting("qinxue")

class decorator with parameters

class logging(object):
    def __init__(self, level="INFO"):
        self.level = level

    def __call__(self, func):
        def wrapper(*args, **kwargs):
            print(self.level + ": func: " + func.__name__)
            func(*args, **kwargs)
        return wrapper

@logging(level="INFO")
def greeting(name):
    print("hello, " + name)

if __name__ == '__main__':
    greeting("qinxue")

A property is a special property that, when accessed, executes a piece of functionality (function) and returns a value. After defining the function of a class as a feature, when the object uses obj.name again, it is impossible to notice that its name is calculated by executing a function. The use of this feature follows the principle of unified access.

import math
class Circle:
    def __init__(self, radius): #圆的半径radius
        self.radius = radius
    @property
    def area(self):
        return math.pi * self.radius**2 #计算面积

    @property
    def perimeter(self):
        return 2*math.pi*self.radius #计算周长

c = Circle(10)

print(c.radius)
print(c.area) #可以像访问数据属性一样去访问area,会触发一个函数的执行,动态计算出一个值
print(c.perimeter) #同上

For classmethod: Whether this method is called from an instance or a class, it passes the class as the first parameter. Specifically, both classes and objects can call it.

For staticmethod (static method): there are often some functions related to the class, but static methods need to be used when the runtime does not require the participation of instances and classes . For example, changing environment variables or modifying properties of other classes can be used static method. This is actually very similar to static functions and static variables in C++. Likewise, both classes and objects can call it.

class Student(object):
    def __init__(self, name):
        self.name = name

    def printName(self):
        print(self.name)

    @staticmethod
    def smethod(*arg):
        print("Statics: ", arg)

    @classmethod
    def cmethod(*arg):
        print("Class: ", arg)

s = Student("qinxue")

s.printName()# qinxue
Student.printName()# 会报错,因为类不可以直接调用非静态函数

s.smethod()# Statics:  ()
Student.smethod()# Statics:  ()


s.cmethod()# Class:  (<class '__main__.Student'>,)
Student.cmethod()# Class:  (<class '__main__.Student'>,)

When there are many decorators to decorate a function, its calling process is as follows:

@a
@b
@c
def f():

equal to

f = a(b(c(f)))

An example is as follows:

from functools import wraps

def a(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print("a: " + func.__name__ + "...")
        return func(*args, **kwargs)
    return wrapper

def b(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print("b: " + func.__name__ + "...")
        return func(*args, **kwargs)
    return wrapper

def c(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print("c: " + func.__name__ + "...")
        return func(*args, **kwargs)
    return wrapper

@a
@b
@c
def f(name):
    print("hello, " + name)

if __name__ == '__main__':
    f("qinxue")

# 输出的结果为:
# a: f...
# b: f...
# c: f...
# hello, qinxue

The above is a brief summary of decorators in python.
The reference articles are as follows:
https://www.zhihu.com/question/26930016
https://www.cnblogs.com/cicaday/p/python-decorator.html
https://www.cnblogs.com/wangyongsong/p/ 6750454.html
https://www.pythoncentral.io/difference-between-staticmethod-and-classmethod-in-python/

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325651985&siteId=291194637