一篇文章让你完全了解python修饰器

说明

因为是跟着视频学习的,视频里说的内容有限,基本上说的内容我都会去网上找资料单独加深扩展一下,但修饰器真的难住我了,听着视频里说 修饰器如何如何牛逼,网上找资料,也说修饰器如何如何牛逼,但就因为修饰器如何如何牛逼,所以我刚开始才连用都没法正确使用吗?
无论是视频还是网上的博客,说修饰器的时候 都只说了修饰器是什么,以及修饰器使用,却没人说 修饰器注意事项,导致刚开始接触修饰器的人 会碰壁,会各种报错。(因为修饰器和我们平常使用def函数无异,但没人说修饰器的使用和普通函数使用方法不一样,所以就这容易误导人)
阅读了很多博客,跟着别人的博客做了很多实验,敲了很多代码,起初都是会有报错,即使是跟着博客内容走的。 因为基本上博客都没有放运行结果图,找的资料也有挺多重复的,而且很多博客代码过于高深,对于我这种连修饰器都无法正确使用的人来说,确实有点为难了。
所以 我结合别人博客的同时,自己也总结了使用方法 和注意事项。 相信看了这篇文章 对于修饰器的逻辑已经可以捋清楚了。
最后再啰嗦一下,学习的时候要先学原理,熟悉原理再扩展,无论是什么语法,都要先学会语法和规则,代码量上去了,语法原理没问题,这时候才开始往高阶进展,所以 初期,网上找资料的时候 要学会主动过滤语法中 看不懂的代码,别花精力去研究里面功能代码的意思,而是花心思研究 语法!

修饰器 从入门到…了解? 自信一点 ,到熟悉。。。

修饰器介绍

装饰器:装饰器本质上是一个函数,该函数用来处理其他函数,它可以让其他函数在不需要修改代码的前提下增加额外的功能(称为装饰器Decoration,类似于AOP),装饰器的返回值也是一个函数对象。它经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等应用场景。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能。
如果文字有点难理解,那么就来一个图 看看修饰器是啥

我们之前所理解的python执行函数过程是如图1.1的流程.
如果我们给函数添加了修饰器(如图1.2),那么当程序执行到函数A的时候,系统会检测到函数A上有一个修饰器,那么系统就会先执行修饰器里的过程然后再回到函数执行函数的内容.
注:从修饰器回来的那一条线路其实并不是必须的, 需要在修饰器里面编写回来的语句回到函数,下面会有说的(二级函数中调用一级函数的参数)
在这里插入图片描述

修饰器的简单作用

说修饰器作用的时候,我们先说说需求吧,什么时候能用得到修饰器。
比如,我现在有一个函数

def ccx():
    print('my name is ccx')
    print('I want to know  the time')

如果我想知道现在时间,普通方法就是在当前函数加功能对吧,所以加上时间代码如下

import time
def ccx():
    print('my name is ccx')
    print('I want to know  the time')
    print(' | ^ - ^ |' * 5)
    start = time.time()
    start1 = time.asctime(time.localtime(time.time()))
    print('当前时间戳为:', start)
    print('当前时间为:', start1)
ccx()

在这里插入图片描述
上面就是在一个函数中 实现添加时间的常规方法,但 如果 我们有10个函数,100个函数需要添加这个时间方法呢??? 复制代码 然后挨个粘贴? 要是这样的话,人生苦短 我用python,这就矛盾了啊。 所以 有了 修饰器的存在。

定义修饰器

修饰器的功能代码 可以自定义,你只需要知道语法,以及怎么定义的就行,后面修饰器的功能代码 你想咋玩咋玩。
语法:

def name(x):
    def name1():
        # 功能代码块
        x() #调用参数,如果不加这个,函数中的内容就不会被调用。
        # 功能代码块
    return name1()

如:现在定义一个 实现打印时间的 修饰器!

# 定义一个显示时间的修饰器
import time # 时间模块
def test(func):
    def wrapper():
        start = time.time()
        start1 = time.asctime(time.localtime(time.time()))
        func() # 用来输出函数内容
        time.sleep(5) #休眠 5秒
        end = time.time()
        end1 = time.asctime(time.localtime(time.time()))
        print('start时间戳为:',start)
        print('start当前时间为:', start1)
        print('end时间戳为:',end)
        print('end当前时间为:',end1)
    return wrapper()

注意,单独定义一个 修饰器,运行是没有任何输出的!!!因为没有调用!
在这里插入图片描述

使用修饰器

方法: 在使用装饰器的函数上面加上 @修饰器名称, python的修饰器是一种语法糖(糖衣语法).
如,现在创建一个 ccx()函数,调用显示时间的修饰器!!!

# 定义一个显示时间的修饰器
import time # 时间模块
def test(func):
    def wrapper():
        start = time.time()
        start1 = time.asctime(time.localtime(time.time()))
        func() # 用来输出函数内容
        time.sleep(5) #休眠 5秒
        end = time.time() # 时间戳
        end1 = time.asctime(time.localtime(time.time())) # 时间
        print('start时间戳为:',start)
        print('start当前时间为:', start1)
        print('end时间戳为:',end)
        print('end当前时间为:',end1)
    return wrapper()

@test # 使用修饰器
def ccx():
    print('my name is ccx')
    print('I want to know  the time')

在这里插入图片描述

注:使用修饰器,不能有调用 该函数,否则就会报错!!! 因为修饰器中已经自带调用了,不能再单独调用!!! 调用后报错如下!!!
在这里插入图片描述

分析一下执行过程,使用修饰器执行效果等于下图:
在这里插入图片描述

扩展内容

下面是一些扩展内容,感兴趣的可以看看研究一下。

在《Python3程序开发指南第二版》(以下简称《指南》)中给的例子是一个对python初学者(没涉及项目)来说比较有趣的小修饰器, 有兴趣可以看看,我给它做了一点注释

def positive_result(function):
    def wrapper(*args, **kwargs):
        # result获得函数的返回值, 进行结果判断
        result = function(*args, **kwargs)
        # assert断言, 如果function函数的返回值大于等于0, 的产生一个AssertionError异常
        assert result >= 0, function.__name__ + "() result isn't >= 0"
        # 返回
        return result
   
    # 将wrapper的docstring和名称设置成和原始函数一样,有利于内省(获得自身的信息)
    wrapper.__name__ = function.__name__ 
    wrapper.__doc__ = function.__doc__
    return wrapper

# 使用positive_result修饰器
@positive_result
def discriminant(a,b,c):
    return (b**2) - (4*a*c)


print(discriminant(0,6,5))
print(discriminant(2,6,5))

《指南》中给出了这个例子的简化版, 使用到了functools.wraps(function)

def positive_result(function):
    # wrapper本身使用functools模块的@functools.wraps进行包裹, 这可以确保wrapper的名称与docstring与function相同
    @functools.wraps(function)
    def wrapper(*args, **kwargs):
        result = function(*args, **kwargs)
        assert result >= 0, function.__name__ + "() result isn't >= 0"

        return result
    return wrapper
  • 修饰器参数化
    现在我们已经了解到了什么是修饰器以及修饰器的基本使用, 那么在上面的日志修饰器上, 我们的日志信息往往是要写入文件内,但是不同的函数需要写进的文件名不一样, 那么简单的 @修饰器名称已经没法满足需要了, 这个时候就需要修饰器参数化, 即将要操作的文件名传递给test()函数
    现在放一个《指南》中给出的例子
import functools
def bounded(mininum, maxinum):
    def decorator(function):
        @functools.wraps(function)
        def wrapper(*args, **kwargs):
            result = function(*args, **kwargs)
            if result < mininum:
                return mininum
            elif result > maxinum:
                return maxinum
            return result
        return wrapper
    return decorator

@bounded(0,100)
def percent(amount, total):
    return (amount / total) * 100

percent(15,100)

执行过程如下:

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/cuichongxin/article/details/106563876