• python当中的装饰器


    1.装饰器

      首先我们来说一下一个软件的设计原则:开闭原则,又被称为开发封闭原则,你的代码对功能的扩展是开放的,你的程序对修改源代码是封闭的.这样的软件设计思路可以更好的维护和开发.

      开放:对功能扩展开放

      封闭:对修改代码封闭

      接下来我们看装饰器.首先我们先模拟一下女娲造人.

    def  create():
        print('女娲很厉害,捏个泥人吹口气就成了人')
    
    create_people()
    

      现在我们让这个函数加个浇水功能.

    def  create():
        print('浇水')
        print('女娲很厉害,捏个泥人吹口气就成了人')
    
    create_people()
    

      但是,现在就违反了开闭原则,如果每次要添加新功能,那这样就等于是修改了源代码.因为开闭原则对修改是封闭的,那怎么办,我们可以这样做

    def water():
        print('先浇水')
        create()
    water()
    

      这样做的问题是.函数写好了,重新创建了这个函数,在这之前访问过这个函数的人必须要修改代码来访问新的函数water(),所以现在就用到了装饰器,装饰器的作用就是在不修改原有代码的基础上,给函数扩展功能

    def create_people():
        print('女娲很厉害随随便便就造人')
    def water(fn):
        def inner():
            print('浇浇水')
            fn()
            print('施肥')
        return inner
    
    # # create_people() # 这个就不行了.
    # warter() # 访问浇水就好了
    
    # func = water(create_people)
    # func() # 有人问了. 下游访问的不不依然是func么, 不不还是要改么?
    create_people = water(create_people)
    create_people() # 这回就好了吧
    

     说一下执行流程:

    结论: 我们使用water函数把create_people给包装了一下. 在不修改create_people的前提下.
    完成了对create_people函数的功能添加

     

    下面我们给出装饰器的完整模型代码

    def wrapper(fn):  # fn是目标函数
        def inner(*args,**kwargs):  # 为了目标函数的传参
            '''这里是执行函数前的内容'''
                ret = fn(*args,**kwargs)  # 调用目标函数,ret是目标函数的返回值
            '''这里是执行目标函数后的内容
                return ret  # 把目标函数的返回值返回.保证函数正常的结束
        return inner
    
    @wrapper  # 这里是语法糖
    def func():
        print('我是目标函数")
    
    func() # 调用目标函数
    

      

    二.带参数的装饰器

      注意: 咱们之前的写法是@wrapper 其中wrapper是一个函数. 那么也就是说. 如果我能让wrapper这里换成个函数就行了了. wrapper(True)返回的结果是wrapper也是⼀一个函数啊. 刚刚

    好和前面的@组合成一个@wrapper. 依然还是原来那个装饰器. 只不过这里套了3层. 但你要
    能看懂. 其实还是原来那个装饰器@wrapper

      

      执行步骤:    

        先执行wrapper(True) 然后再@返回值. 返回值恰好是wrapper. 结果就是@wrapper

    下面是装饰器传参的通用写法

    def wrapper_out(flag):
        def wrapper(fn):
            def inner(*args,**kwargs):
                if flag == True:
                    '''执行目标函数前的位置'''
                    ret = fn(*args,**kwargs)
                    '''执行目标函数后的位置'''
                    return ret
                else:
                    ret = fn(*args,**args)
                    return ret
            return inner
        return wrapper
    
    @wrapper_out(True)
    def func():
        print('这里是目标函数')
        
    func()
                    
    

      

    三.多个装饰器装饰同一个函数

    def wrapper1(fn):
        def inner(*args,**kwargs):
            print('111')
            ret = fn(*args,**kwargs)
            print('222')
        return inner
    
    def wrapper2(fn):
        def inner(*args,**kwargs):
            print('333')
            ret = fn(*args,**kwargs)
            print('444')
        return inner
    
    def wrapper3(fn):
        def inner(*args,**kwargs):
            print('555')
            ret = fn(*args,**kwargs)
            print('666')
        return inner
    
    @wrapper1
    @wrapper2
    @wrapper3
    def eat():
        print('我想吃水果')
    
    eat()

      

       执行顺序: 首先@wrapper1装饰起来. 然后获取到⼀个新函数是wrapper1中的inner. 然后执行@wrapper2.这个时候. wrapper2装饰的就是wrapper1中的inner了了. 所以. 执行顺序就像:第⼆层装饰器前(第⼀层装饰器前(目标)第一层装饰器后)第二层装饰器后. 程序从左到右执行起来. 就是我们看到的结果

     

     四.  使用wraps装饰函数保留函数的原名称

    1.wraps

    装饰器装饰的函数名字会变为inner,为了让装饰的函数名还原,我们用到了@wraps,这样打印出的函数名就还是原来的函数名了, 要引入functools模块

    from functools import wraps # 可以改变一个函数的名字, 注释...
     
    def wrapper(fn):
        @wraps(fn)  # 把inner的名字改变成原来的func
        def inner(*args, **kwargs):
            print("前")
            ret = fn(*args, **kwargs)
            print("后")
            return ret
     
        return inner
     
    @wrapper # func = wrapper(func)
    def func():
        print('哈哈哈')
     
    print(func.__name__) # func
    

      

  • 相关阅读:
    CentOS6.0 yum php mcrypt 扩展安装问题
    WordPress入门系列之基本设置
    ./configure 配置文件时出错checking for g++... no
    锐捷硬件防火墙
    CentOS 安装php mcrypt和mbstring的扩展
    (转)在asp.net 2.0中使用SqlBulkCopy类迁移数据
    正则表达式对象&&String对象
    SQL Server 和 SQLite 时间函数汇总
    FreeBSD下nginx并支持php配置详解
    从Ports安装MySQL
  • 原文地址:https://www.cnblogs.com/robertx/p/10121428.html
Copyright © 2020-2023  润新知