查看原文
其他

Python装饰器(你想知道的这里都有)

(给Python开发者加星标,提升Python技能

来源:  南枝向暖北枝寒MA

https://blog.csdn.net/mall_lucy/article/details/108791699

【导读】:装饰器(Decorators)是 Python 的一个重要部分。简单地说:它们是修改其他函数的功能的函数。有助于让我们的代码更简短,也更Pythonic(Python范儿)。很多Pythoner不知道在哪儿使用它们,哪些区域里装饰器可以让代码更简洁。通过本文的学习让你理解并学会装饰器,让你更Pythonic!来一起学习吧。


--- 以下是正文 ---


1. 装饰器的定义


就是给已有函数增加额外功能的函数,它本质上就是一个闭包函数。装饰器的功能特点:


1、不修改已有函数的源代码

2、不修改已有函数的调用方式

3、给已有函数增加额外的功能

4、闭包和装饰器的区分:


如果闭包函数的参数有且只有一个,并且是函数类型,那么这个闭包函数称之为装饰器。写代码要遵循开放封闭原则,它规定已经实现的功能代码不允许被修改,但可以被扩展。


2. 装饰器的示例代码

   

1

# 定义装饰器

2

def decorator(func):

3

    def inner():

4

        # 在内部函数里面对已有函数进行装饰

5

        print('已添加登录认证')

6

        func()

7

 

8

    return inner

9

 

10

 

11

def comment():

12

    print('发表评论')

13

 

14

 

15

# 调用装饰器对已有函数进行装饰,左边的comment=inner

16

comment = decorator(comment)

17

 

18

# 调用方式不变

19

comment()


3. 装饰器的语法糖写法


如果有多个函数都需要添加登录验证的功能,每次都需要编写func = decorator(func)这样代码对已有函数进行装饰,这种做法还是比较麻烦。

Python给提供了一个装饰函数更加简单的写法,那就是语法糖,语法糖的书写格式是: @装饰器名字,通过语法糖的方式也可以完成对已有函数的装饰。

   

1

# 定义装饰器

2

def decorator(func):

3

    def inner():

4

        # 在内部函数里面对已有函数进行装饰

5

        print('已添加登录认证')

6

        func()

7

 

8

    return inner

9

 

10

 

11

@decorator # comment = decorator(comment) 装饰器语法糖对该代码进行了封装 左边comment=inner

12

def comment():

13

    print('发表评论')

14

 

15

 

16

# 调用方式不变

17

comment()


4. 装饰器的执行时机


当前模块加载完成以后,装饰器会立即执行,对已有函数进行装饰。

1

# 定义装饰器

2

def decorator(func):

3

    print('装饰器执行了')

4

 

5

    def inner():

6

        # 在内部函数里面对已有函数进行装饰

7

        print('已添加登录认证')

8

        func()

9

 

10

    return inner

11

 

12

 

13

@decorator # comment = decorator(comment) 装饰器语法糖对该代码进行了封装 左边comment=inner

14

def comment():

15

    print('发表评论')


5. 装饰器的使用


5.1 装饰器的使用场景

    1.函数执行时间的统计。

    2.输出日志信息。


5.2 装饰器实现已有函数执行时间的统计

   

1

import time

2

 

3

 

4

def decorator(func):

5

    def inner():

6

        # 获取时间距离1970-1-1 0:0:1的时间差

7

        begin = time.time()

8

        func()

9

        end = time.time()

10

        result = end - begin

11

        print(f'函数执行完成耗时:{result}')

12

 

13

    return inner

14

 

15

 

16

@decorator

17

def work():

18

    for i in range(10000):

19

        print(i)

20

 

21

 

22

work()

6. 通用装饰器的使用


通用装饰器:可以装饰任意类型的函数。使用装饰器装饰已有函数的时候,内部函数的类型和要装饰的已有函数的类型保持一致。


6.1 装饰带有参数的函数


1

def decorator(func):

2

    def inner(num1, num2):

3

        print('正在努力执行加法计算')

4

        func(num1, num2)

5

 

6

    return inner

7

 

8

 

9

@decorator

10

def add_num(num1, num2):

11

    result = num1 + num2

12

    print(f'结果为:{result}')

13

 

14

 

15

add_num(1, 2)


6.2 装饰带有参数、返回值的函数


1

def decorator(func):

2

    def inner(num1, num2):

3

        print('正在努力执行加法计算')

4

        num = func(num1, num2)

5

        return num

6

 

7

    return inner

8

 

9

 

10

@decorator

11

def add_num(num1, num2):

12

    result = num1 + num2

13

    return result

14

 

15

 

16

result = add_num(1, 2)

17

print(f'结果为:{result}')


6.3 装饰带有不定长参数、返回值的函数


1

def decorator(func):

2

    def inner(*args, **kwargs):

3

        print('正在努力执行加法计算')

4

        # *args:把元组里面的每一个元素,按照位置参数的方式进行传参

5

        # **kwargs:把字典里面的每一个键值对,按照关键字的方式进行传参

6

        num = func(*args, **kwargs)

7

        return num

8

 

9

    return inner

10

 

11

 

12

@decorator

13

def add_num(*args, **kwargs):

14

    result = 0

15

    for value in args:

16

        result += value

17

    for value in kwargs.values():

18

        result += value

19

    return result

20

 

21

 

22

result = add_num(1, 2, a=3)

23

print(f'结果为:{result}')


7. 多个装饰器的使用


多个装饰器的装饰过程:由内到外的一个装饰过程,先执行内部的装饰器,在执行外部的装饰器。


1

def make_div(func):

2

    print('make_div装饰器执行了')

3

 

4

    def inner():

5

        result = '<div>' + func() + '</div>'

6

        return result

7

 

8

    return inner

9

 

10

 

11

def make_p(func):

12

    print('make_p装饰器执行了')

13

 

14

    def inner():

15

        result = '<p>' + func() + '</p>'

16

        return result

17

 

18

    return inner

19

 

20

 

21

# 原理剖析:content = make_div(make_p(content))

22

# 分布拆解:content = make_p(content),内部装饰器完成,content = make_p.inner

23

# content = make_div(make_p.inner)

24

@make_div

25

@make_p

26

def content():

27

    return '人生苦短,我用python'

28

 

29

 

30

c = content()

31

print(c)


1

make_p装饰器执行了

2

make_div装饰器执行了

3

<div><p>人生苦短,我用python</p></div>


8. 带有参数的装饰器


带有参数的装饰器就是使用装饰器装饰函数的时候可以传入指定参数,语法格式: @装饰器(参数,…)。使用带有参数的装饰器,其实是在装饰器外面又包裹了一个函数,使用该函数接收参数,返回是装饰器,因为 @ 符号需要配合装饰器实例使用。


1

def return_decorator(flag):

2

    # 装饰器只能接收一个参数并且是函数类型

3

    def decorator(func):

4

        def inner(a, b):

5

            if flag == '+':

6

                print('正在努力执行加法计算')

7

            elif flag == '-':

8

                print('正在努力执行减法计算')

9

            func(a, b)

10

 

11

        return inner

12

 

13

    # 当调用函数的时候可以返回一个装饰器decorator

14

    return decorator

15

 

16

 

17

@return_decorator('+') # decorator = return_decorator('+'), @decorator => add_num = decorator(add_num)

18

def add_num(a, b):

19

    result = a + b

20

    print(result)

21

 

22

 

23

@return_decorator('-')

24

def sub_num(a, b):

25

    result = a - b

26

    print(result)

27

 

28

 

29

add_num(1, 2)

30

sub_num(1, 2)

9. 类装饰器的使用


类装饰器:使用类装饰已有函数


1

class MyDecorator(object):

2

    def __init__(self, func):

3

        self.__func = func

4

 

5

    # 实现__call__方法,表示对象是一个可调用对象,可以像调用函数一样进行调用

6

    def __call__(self, *args, **kwargs):

7

        # 对已有函数进行封装

8

        print('马上就有下班啦')

9

        self.__func()

10

 

11

 

12

@MyDecorator # @MyDecorator => show = MyDecorator(show)

13

def show():

14

    print('快要下雪啦')

15

 

16

 

17

# 执行show,就相当于执行MyDecorator类创建的实例对象,show() => 对象()

18

show()


1

马上就有下班啦

2

快要下雪啦

扩展:

函数之所以能够调用,是因为函数内部实现了 __call__ 方法。


10. 装饰器应用场景


1、收集函数的操作或错误日志记录。

2、验证函数的使用权限。

3、计算函数的运行时间。

4、在ORM/DB模型操作时,通过属性方法动态地获取关联的数据。

5、函数数据的缓存。

6、定制函数的输入和输出(序列化和反序列化)。



- EOF -

推荐阅读  点击标题可跳转

1、PEP 318:函数和方法的装饰器

2、Python 3.9 新特性:任意表达式可作为装饰器

3、PEP-3129 类装饰器


觉得本文对你有帮助?请分享给更多人

关注「Python开发者」加星标,提升Python技能

点赞和在看就是最大的支持❤️

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存