Python decorator usage

Python decorator usage example summary

1. What is a decorator

Python's decorator is essentially a Python function, which allows other functions to add additional functions without any code changes. The return value of the decorator is also a function object. Simply put, a decorator is a function that returns a function.

It is often used in scenarios with aspect requirements, such as inserting logs, performance testing, transaction processing, caching, and permission verification. Decorator is an excellent design to solve this kind of problem. With decorator, we can extract a lot of similar 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 additional functions to an existing object.

2. Why do you need a decorator

1. First look at a simple example:

1

2

def foo():

print('i am foo')

2. Increase demand

Now there is a new requirement, hoping to record the execution log of the function, so add the log code to the code:

1

2

3

def foo():

  print('i am foo')

  print("foo is running")

3. There is another demand

Suppose that there are 100 functions that need to increase this requirement, and the requirement of printing logs before execution may be added to these 100 functions in the future, what should be done? Have you changed it one by one?

Of course not. This will cause a lot of similar codes. In order to reduce repetitive code writing, we can do this and redefine a function: specifically process the log, and execute the real business code after the log is processed.

1

2

3

4

5

6

7

8

9

def use_logging(func):

  print("%s is running" % func.__name__)

  func()

def bar():

  print('i am bar')

use_logging(bar)

#result:

#bar is running

#i am bar

Through the above use_logging function, we have added the log function. No matter how many functions need to increase the log or modify the format of the log in the future, we only need to modify the use_logging function and execute the use_logging (decorated function) to achieve the desired effect.

1

2

3

4

5

6

7

def use_logging(func):

  print("%s is running" % func.__name__)

  return func

@use_logging

def bar():

  print('i am bar')

bar()

3. Getting started with basic decorators

1. Syntactic sugar for decorators

Python provides the @ symbol as syntactic sugar for decorators, making it more convenient for us to apply decorating functions. But using syntactic sugar requires that the decorated function must return a function object. Therefore, we wrap the above func function with an embedded function and return.

The decorator is equivalent to executing the decorated function use_loggin and then returning to the decorated function bar, so when bar() is called, it is equivalent to executing two functions. Equivalent to use_logging(bar)()

1

2

3

4

5

6

7

8

9

def use_logging(func):

  def _deco():

    print("%s is running" % func.__name__)

    func()

  return _deco

@use_logging

def bar():

  print('i am bar')

bar()

2. Decorate functions with parameters

Now our parameters need to pass in two parameters and calculate the value, so we need to change the inner function to pass in our two parameters a and b, which is equivalent to use_logging(bar)(1,2)

1

2

3

4

5

6

7

8

9

def use_logging(func):

  def _deco(a,b):

    print("%s is running" % func.__name__)

    func(a,b)

  return _deco

@use_logging

def bar(a,b):

  print('i am bar:%s'%(a+b))

bar(1,2)

我们装饰的函数可能参数的个数和类型都不一样,每一次我们都需要对装饰器做修改吗?这样做当然是不科学的,因此我们使用python的变长参数*args和**kwargs来解决我们的参数问题。

3、函数参数数量不确定

不带参数装饰器版本,这个格式适用于不带参数的装饰器。

经过以下修改我们已经适应了各种长度和类型的参数。这个版本的装饰器已经可以任意类型的无参数函数。

1

2

3

4

5

6

7

8

9

10

11

12

13

def use_logging(func):

  def _deco(*args,**kwargs):

    print("%s is running" % func.__name__)

    func(*args,**kwargs)

  return _deco

@use_logging

def bar(a,b):

  print('i am bar:%s'%(a+b))

@use_logging

def foo(a,b,c):

  print('i am bar:%s'%(a+b+c))

bar(1,2)

foo(1,2,3)

4、装饰器带参数

带参数的装饰器,这个格式适用于带参数的装饰器。

某些情况我们需要让装饰器带上参数,那就需要编写一个返回一个装饰器的高阶函数,写出来会更复杂。比如:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

#! /usr/bin/env python

# -*- coding:utf-8 -*-

# __author__ = "TKQ"

def use_logging(level):

  def _deco(func):

    def __deco(*args, **kwargs):

      if level == "warn":

        print "%s is running" % func.__name__

      return func(*args, **kwargs)

    return __deco

  return _deco

@use_logging(level="warn")

def bar(a,b):

  print('i am bar:%s'%(a+b))

bar(1,3)

# 等价于use_logging(level="warn")(bar)(1,3)

5、functools.wraps

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

def use_logging(func):

  def _deco(*args,**kwargs):

    print("%s is running" % func.__name__)

    func(*args,**kwargs)

  return _deco

@use_logging

def bar():

  print('i am bar')

  print(bar.__name__)

bar()

#bar is running

#i am bar

#_deco

#函数名变为_deco而不是bar,这个情况在使用反射的特性的时候就会造成问题。因此引入了functools.wraps解决这个问题。

使用functools.wraps:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

import functools

def use_logging(func):

  @functools.wraps(func)

  def _deco(*args,**kwargs):

    print("%s is running" % func.__name__)

    func(*args,**kwargs)

  return _deco

@use_logging

def bar():

  print('i am bar')

  print(bar.__name__)

bar()

#result:

#bar is running

#i am bar

#bar ,这个结果是我们想要的。OK啦!

6、实现带参数和不带参数的装饰器自适应

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

import functools

def use_logging(arg):

  if callable(arg):#判断参入的参数是否是函数,不带参数的装饰器调用这个分支

    @functools.wraps(arg)

    def _deco(*args,**kwargs):

      print("%s is running" % arg.__name__)

      arg(*args,**kwargs)

    return _deco

  else:#带参数的装饰器调用这个分支

    def _deco(func):

      @functools.wraps(func)

      def __deco(*args, **kwargs):

        if arg == "warn":

          print "warn%s is running" % func.__name__

        return func(*args, **kwargs)

      return __deco

    return _deco

@use_logging("warn")

# @use_logging

def bar():

  print('i am bar')

  print(bar.__name__)

bar()

三、类装饰器

使用类装饰器可以实现带参数装饰器的效果,但实现的更加优雅简洁,而且可以通过继承来灵活的扩展.

1、类装饰器

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

class loging(object):

  def __init__(self,level="warn"):

    self.level = level

  def __call__(self,func):

    @functools.wraps(func)

    def _deco(*args, **kwargs):

      if self.level == "warn":

        self.notify(func)

      return func(*args, **kwargs)

    return _deco

  def notify(self,func):

    # logit只打日志,不做别的

    print "%s is running" % func.__name__

@loging(level="warn")#执行__call__方法

def bar(a,b):

  print('i am bar:%s'%(a+b))

bar(1,3)

2、继承扩展类装饰器

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

class email_loging(Loging):

  '''

  一个loging的实现版本,可以在函数调用时发送email给管理员

  '''

  def __init__(self, email='[email protected]', *args, **kwargs):

    self.email = email

    super(email_loging, self).__init__(*args, **kwargs)

  def notify(self,func):

    # 发送一封email到self.email

    print "%s is running" % func.__name__

    print "sending email to %s" %self.email

@email_loging(level="warn")

def bar(a,b):

  print('i am bar:%s'%(a+b))

bar(1,3)

Guess you like

Origin blog.csdn.net/grl18840839630/article/details/110135202