python装饰器,一文秒懂

装饰器

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

def decorator(func):
    def inner(name):
        # 在内部函数里面对已有函数进行装饰
        print('已添加登录认证')
        func(name)
    return inner

def comment(name):
    print(' {} 发表评论'.format(name))
comment = decorator(comment)
# 将comment函数装饰成decorator函数里边的inner函数

正式讲装饰器之前,我们要先插入闭包这一个概念。

闭包 : 函数内的函数以及其自由变量形成闭包。也即闭包时一个保留定义函数时存在的自由变量的绑定的函数,这样在调用函数时,绑定的自由变量依旧可用!

闭包有什么好处呢? 闭包可以避免全局变量的使用以及提供某种形式数据的隐藏。当函数中的变量和函数较少且其中某个功能常用时,使用闭包来封装。当变量和函数更加复杂时,则使用类来实现。来一个栗子吧!

# 计算移动平均值的函数	
def make_averager():
    series = []
   
    def averager(new_value):
        series.append(new_value)
        total = sum(series)
        return total/len(series)
    return averager
	// 在这里边 series = [] 到第八行 return total/len(series)为闭包,变量series为averager()函数中的自由变量!

1、装饰器的定义

就是给已有函数增加额外功能的函数,它本质上就是一个闭包函数。
装饰器的功能特点:
1、不修改已有函数的源代码
2、不修改已有函数的调用方式
3、给已有函数增加额外的功能
4、被装饰的目标函数执行之前,会先执行装饰部分的代码逻辑,然后执行目标函数逻辑

闭包和装饰器的区分
如果闭包函数的参数有且只有一个,并且是函数类型,那么这个闭包函数称之为装饰器。

写代码要遵循开放封闭原则,它规定已经实现的功能代码不允许被修改,但可以被扩展。

2. 装饰器的示例代码

# 定义装饰器
def decorator(func):
    def inner():
        # 在内部函数里面对已有函数进行装饰
        print('已添加登录认证')
        func()
    return inner

def comment():
    print('发表评论')
# 调用装饰器对已有函数进行装饰,左边的comment=inner
comment = decorator(comment)
# 调用方式不变
comment()

3. 装饰器的语法糖写法

如果有多个函数都需要添加登录验证的功能,每次都需要编写func = decorator(func)这样代码对已有函数进行装饰,这种做法还是比较麻烦。
Python给提供了一个装饰函数更加简单的写法,那就是语法糖,语法糖的书写格式是: @装饰器名字,通过语法糖的方式也可以完成对已有函数的装饰。

# 定义装饰器
def decorator(func):
    def inner():
        # 在内部函数里面对已有函数进行装饰
        print('已添加登录认证')
        func()
    return inner

@decorator  # comment = decorator(comment) 装饰器语法糖对该代码进行了封装 左边comment=inner
def comment():
    print('发表评论')
# 调用方式不变
comment()

4. 装饰器的执行时机

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

5. 装饰器的使用

5.1 装饰器的使用场景

1.函数执行时间的统计。
2.输出日志信息。
3.不变动原有函数,实现输入参数的处理工作

5.2 装饰器实现已有函数执行时间的统计
import time
def decorator(func):
    def inner():
        # 获取时间距离1970-1-1 0:0:1的时间差
        begin = time.time()
        func()
        end = time.time()
        result = end - begin
        print(f'函数执行完成耗时:{result}')
    return inner

@decorator
def work():
    for i in range(10000):
        print(i)
        
work()

6. 通用装饰器的使用

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

6.1 装饰带有参数的函数

def decorator(func):
    def inner(num1, num2):
        print('正在努力执行加法计算')
        func(num1, num2)
    return inner

@decorator
def add_num(num1, num2):
    result = num1 + num2
    print(f'结果为:{result}')

add_num(1, 2)

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

def decorator(func):
    def inner(num1, num2):
        print('正在努力执行加法计算')
        num = func(num1, num2)
        return num
    return inner

@decorator
def add_num(num1, num2):
    result = num1 + num2
    return result

result = add_num(1, 2)
print(f'结果为:{result}')

输出:

正在努力执行加法计算
结果为:3

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

def decorator(func):
    def inner(*args, **kwargs):
        print('正在努力执行加法计算...')
        print("args={}, kwargs={}".format(args, kwargs))
        # *args:把元组里面的每一个元素,按照位置参数的方式进行传参
        # **kwargs:把字典里面的每一个键值对,按照关键字的方式进行传参
        num = func(*args, **kwargs)
        return num
    return inner

@decorator
def add_num(*args, **kwargs):
    result = 0
    for value in args:
        result += value
    for value in kwargs.values():
        result += value
    return result

result = add_num(1, 2, a=3, b=7)
print(f'结果为:{result}')

输出结果:

正在努力执行加法计算
args=(1, 2), kwargs={'a': 3, 'b': 7}
结果为:13

7. 多个装饰器的使用

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

def make_div(func):
    print('make_div装饰器执行了')

    def inner():
        result = '<div>' + func() + '</div>'
        return result
    return inner

def make_p(func):
    print('make_p装饰器执行了')

    def inner():
        result = '<p>' + func() + '</p>'
        return result
    return inner

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

@make_div
@make_p
def content():
    return '人生苦短,我用python'

c = content()
print(c)

输出:

>>> make_p装饰器执行了
>>> make_div装饰器执行了
>>> <div><p>人生苦短,我用python</p></div>

8. 带有参数的装饰器

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

def return_decorator(flag):
    # 装饰器只能接收一个参数并且是函数类型
    def decorator(func):
        def inner(a, b):
            if flag == '+':
                print('正在努力执行加法计算')
            elif flag == '-':
                print('正在努力执行减法计算')
            func(a, b)
        return inner
    # 当调用函数的时候可以返回一个装饰器decorato
    return decorator

@return_decorator('+')  # decorator = return_decorator('+'), @decorator => add_num = decorator(add_num)
def add_num(a, b):
    result = a + b
    print(result)
    
@return_decorator('-')
def sub_num(a, b):
    result = a - b
    print(result)

add_num(1, 2)
sub_num(1, 2)

9. 类装饰器的使用

类装饰器:使用类装饰已有函数class MyDecorator(object):

def __init__(self, func):
    self.__func = func

# 实现__call__方法,表示对象是一个可调用对象,可以像调用函数一样进行调用
def __call__(self, *args, **kwargs):
    # 对已有函数进行封装
    print('马上就有下班啦')
    self.__func()
@MyDecorator  # @MyDecorator => show = MyDecorator(show)
def show():
    print('快要下雪啦')
# 执行show,就相当于执行MyDecorator类创建的实例对象,show() => 对象()
show()

输出:

马上就有下班啦
快要下雪啦

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

10. 装饰器应用场景

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

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

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

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

5、函数数据的缓存。

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

  • 55
    点赞
  • 322
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值