zoukankan      html  css  js  c++  java
  • 装饰器函数

    装饰器的概念应用场景

    概念:装饰器本质上就是一个函数, 它可以让其他函数在不需要做任何代码变动的前提下, 增加额外的功能, 装饰器的返回值也是一个函数对象.

    应用场景: 比如插入日志, 性能测试, 事物处理, 缓存等等场景.

    装饰器的形成过程

    现在我有一个需求: 需要测试一个函数的执行时间, 在不改变这个函数代码的情况下:

    import time
    
    
    def timmer(fn):
        def inner():
            s_time = time.time()
            fn()
            e_time = time.time()
            print('函数运行时间为:', e_time-s_time)
        return inner
    
    
    def func():
        time.sleep(1)
        print('10000行代码运行结束')
    
    
    func = timmer(func)
    func()
    装饰器_简单版

     但是如果有多个函数, 我都想测试他们的执行时间, 那岂不是每次都得func = timmer(func)? 这样感觉还是有点麻烦, 因为这些函数的函数名都不一样.

    所以python给我们提供了更简单的方法, 那就是语法糖.

    import time
    
    
    def timmer(fn):
        def inner():
            s_time = time.time()
            fn()
            e_time = time.time()
            print('函数运行时间为:', e_time-s_time)
        return inner
    
    @timmer     #相当于  func = timmer(func)
    def func():
        time.sleep(1)
        print('10000行代码运行结束')
    
    
    func()
    装饰器_语法糖

    上面的装饰器装饰的都是不带参数的函数, 那如果要装饰一个带参数的函数该怎么办?

    import time
    
    
    def timmer(fn):
        def inner(*args, **kwargs):
            s_time = time.time()
            ret = fn(*args, **kwargs)
            e_time = time.time()
            print('函数运行时间为:', e_time-s_time)
            return ret
        return inner
    
    @timmer     #相当于  func1 = timmer(func1)
    def func1(a,b):
        time.sleep(1)
        print('10000行代码运行结束')
        return a+b
    
    
    @timmer     #相当于  func2 = timmer(func2)
    def func2(x,y):
        time.sleep(1)
        print('10000行代码运行结束')
        return x*y
    
    
    print(func1(2,3))
    print(func2(4,5))
    装饰带参数带返回值的函数

    上面的装饰器已经非常完美了, 但是有我们正常情况下查看函数信息的方法在此都会失效:

    @timmer
    def func1(a,b):
        """我是func1"""
        time.sleep(1)
        print('10000行代码运行结束')
        return a+b
    
    print(func1.__doc__)
    print(func1.__name__)
    
    # 结果:
    # None
    # inner

    解决方案:

    from functools import wraps
    import time
    
    
    def timmer(fn):
        @wraps(fn)
        def inner(*args, **kwargs):
            s_time = time.time()
            ret = fn(*args, **kwargs)
            e_time = time.time()
            print('函数运行时间为:', e_time-s_time)
            return ret
        return inner
    
    @timmer
    def func1(a,b):
        """我是func1"""
        time.sleep(1)
        print('10000行代码运行结束')
        return a+b
    
    print(func1.__doc__)
    print(func1.__name__)
    
    # 结果:
    # 我是func1
    # # func1

    开放封闭原则

    1.对扩展是开放的

    为什么要对扩展开放呢?

    我们说,任何一个程序,不可能在设计之初就已经想好了所有的功能并且未来不做任何更新和修改。所以我们必须允许代码扩展、添加新功能。

    2.对修改是封闭的

    为什么要对修改封闭呢?

    因为我们写的一个函数, 很有可能已经交付给其他人使用了, 如果我们这个时候对其进行修改, 很有可能影响其他已经在使用该函数的用户.

    装饰器完美的遵循了这个开放封闭原则

    装饰器的主要功能和固定架构

    def wrapper(fn):
        def inner(*args, **kwargs):
            """执行函数前要做的"""
            ret=fn(*args, **kwargs)
            """执行函数后要做的"""
            return ret
        return inner
    装饰器的固定格式
    from functools import wraps
    
    def wrapper(fn):
        @wraps(fn)
        def inner(*args, **kwargs):
            """执行函数前要做的"""
            ret=fn(*args, **kwargs)
            """执行函数后要做的"""
            return ret
        return inner
    装饰器的固定格式_wraps版

    带参数的装饰器

    假如你有成千上万个函数使用了一个装饰器, 现在你又想把这些装饰器全部取消掉, 那该怎么办?

    总不可能一个一个去删掉吧, 万一过两天你又想加上去呢?

    import time
    
    def outer(flag):
        def timmer(fn):
            def inner(*args, **kwargs):
                if flag:
                    print("执行函数前要做的")
                ret = fn(*args, **kwargs)
                if flag:
                    print("执行函数后要做的")
                return ret
            return inner
        return timmer
    
    flag = 0
    
    @outer(flag)
    def func1(a,b):
        """我是func1"""
        time.sleep(1)
        print('10000行代码运行结束')
        return a+b
    
    print(func1(2,3))
    带参数的装饰器

    多个装饰器装饰一个函数

    def wrapper1(func):  # func == f 函数名
        def inner1():
            print('wrapper1 ,before func')  # 2
            func()
            print('wrapper1 ,after func')  # 4
        return inner1
    
    def wrapper2(func):  # func == inner1
        def inner2():
            print('wrapper2 ,before func')  # 1
            func()
            print('wrapper2 ,after func')  # 5
        return inner2
    
    # 就近原则
    @wrapper2  # f = wrapper2(f) 里面f == inner1  外面的f == inner2
    @wrapper1  # f = wrapper1(f) 里面的f == 函数名f   外面的f == inner1
    def f():
        print('in f')  # 3
    f()  # inner2()
    
    # 结果:
    # wrapper2 ,before func
    # wrapper1 ,before func
    # in f
    # wrapper1 ,after func
    # wrapper2 ,after func
    多个装饰器装饰一个函数

    装饰器的简单应用举例

    需求:在第一次进入博客园的页面之前需要先登录账户.

    dic_status = {
        'username': None,
        'status': False,
    }
    
    def login(fn):
        def inner(*args, **kwargs):
            if dic_status['status'] == True:
                fn(*args, **kwargs)
                return
            else:
                username=input('用户名:')
                password=input('密码:')
                with open('userinfo',mode='r',encoding='utf-8') as f:
                    for line in f:
                        lis = line.strip().split("|")
                        if username == lis[0] and password == lis[1]:
                            dic_status['username']=username
                            dic_status['status']=True
                            break
                    else:
                        print("登录失败")
                inner(*args, **kwargs)
        return inner
    
    
    
    
    
    @login
    def article():
        print('欢迎登录文章页面')
    @login
    def diary():
        print('欢迎登录日记页面')
    @login
    def comment():
        print('欢迎登录评论页面')
        
        
    article()
    comment()
    简单应用示例
  • 相关阅读:
    java web 入门实例servlet篇(显示后台数据库列表,删除某一条记录并显示)
    我的成长比价系列:java web开发过程中遇到的错误一:sql语句换行错误
    spring mvc + velocity 搭建实例程序maven版本并且使用的是tomcat容器而不是jetty(step by step)
    spring mvc学习笔记(一)web.xml文件配置的一点重要信息
    与数据库连接的页面增删改查 的easyui实现(主要是前端实现)
    oracle 11g 空表导出
    EMCA和EMCTL的简单用法
    vs2010补丁
    CAS 策略已被 .NET Framework 弃用
    sqlserver2008 链接服务器 2000
  • 原文地址:https://www.cnblogs.com/af1y/p/10159200.html
Copyright © 2011-2022 走看看