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

    装饰器

    一、无参装饰器

    1.1什么是装饰器

    器指的是工具,而程序中的函数就是具备某一功能的工具,所以装饰器指的是为被装饰器对象添加额外功能。因此定义装饰器就是定义一个函数,只不过该函数的功能是用来为其他函数添加额外的功能

    需要注意的是:

    • 装饰器本身其实是可以任意可调用的对象
    • 被装饰的对象也可以是任意可调用的对象

    1.2为什么要用装饰器

    如果我们已经上线了一个项目,我们需要修改某一个方法,但是我们不想修改方法的使用方法,这个时候可以使用装饰器。因为软件的维护应该遵循开放封闭原则,即软件一旦上线运行后,软件的维护对修改源代码是封闭的,对扩展功能指的是开放的。

    装饰器的实现必须遵循两大原则

    1. 不修改被装饰对象的源代码
    2. 不修改被装饰对象的调用方式

    装饰器其实就是在遵循以上两个原则的前提下为被装饰对象添加新功能。

    1.3怎么用装饰器

    改变源代码

    我们简单的理解装饰器就是原本的函数增加功能,即给被装饰对象增加功能,怎么做呢,就是将被装饰对象的函数名,当作参数的形式传入装饰器函数中,然后将装饰器函数赋值给一个与被装饰对象函数名相同的变量名,以此来达到以假乱真的目的

    import time
    
    
    def index():
        start = time.time()
        print('welcome to index')
        time.sleep(1)
        end = time.time()
        print(F"index run time is {start-end}")
    
    
    index()
    

    welcome to index
    index run time is -1.0005733966827393

    编写重复代码

    import time
    
    
    def index():
        print('welcome to index')
        time.sleep(1)
    
    
    def f2():
        print('welcome to index')
        time.sleep(1)
    
    
    start = time.time()
    index()
    end = time.time()
    print(F"index run time is {start-end}")
    
    start = time.time()
    f2()
    end = time.time()
    print(F"f2 run time is {start-end}")
    

    welcome to index
    index run time is -1.0002689361572266
    welcome to index
    f2 run time is -1.0004284381866455

    第一种传参方式:改变了调用方式

    import time
    
    
    def index():
        print('hello word')
        time.sleep(1)
    def time_count(func):
        start = time.time()
        func()
        end = time.time()
        print(f'{func} time is {start-end}')
    
    time_count(index)
    

    hello word
    <function index at 0x000001ED6036A950> time is -1.0002200603485107

    第二种传参方式:包给函数-外包

    import time
    
    
    def index():
        print('hello word')
        time.sleep(1)
    
    
    def time_count(func):
        def wrapper():
            start = time.time()
            func()
            end = time.time()
            print(f'{func} time is {start - end}')
    
        return wrapper
    
    
    # f = time_count(index)
    # f()
    
    index = time_count(index)  # index为被装饰器函数的内存地址,即index = wrapper
    index()  # wrapper
    

    hello word
    <function index at 0x0000028B2045A950> time is -1.0001304149627686

    1.4完善装饰器

    上述的装饰器,最后调用index()的时候,其实是在调用wrapper(),因此如果原始的index()有返回值的时候,wrapper()函数的返回值应该呵index()的返回值想同,也就是说,我们需要同步原始的index()和wrapper()方法的返回值。

    往往我们的函数是由返回值的,这个时候我们就需要让装饰器函数的返回值与被装饰对象函数的返回相同,

    import time
    
    
    def index():
        print('hello word')
        time.sleep(1)
        return 123
    
    
    def time_count(func):
        def wrapper():
            start = time.time()
            res = func()
            end = time.time()
            print(f'{func} time is {start - end}')
            return res
    
        return wrapper
    
    
    # f = time_count(index)
    # f()
    
    index = time_count(index)  # index为被装饰器函数的内存地址,即index = wrapper
    res = index()
    print(f'res:{res}')
    

    hello word
    <function index at 0x000001AF8FE6A950> time is -1.000152349472046
    res:123

    如果原始的index()方法需要传参,那么我们之前的装饰器是无法实现该功能,由于有wrapper() = index(),所以给wrapper()方法传参即可。

    import time
    
    
    def index():
        print('hello word')
        time.sleep(1)
        return 123
    
    
    def home(name):
        print(f'hello  {name}  my brother')
        time.sleep(1)
        return name
    
    
    def time_count(func):
        def wrapper(*args, **kwargs):
            start = time.time()
            res = func(*args, **kwargs)
            end = time.time()
            print(f'{func} time is {start - end}')
            return res
    
        return wrapper
    
    
    # f = time_count(index)
    # f()
    
    home = time_count(home)  # index为被装饰器函数的内存地址,即index = wrapper
    res = home('egon')
    print(f'res:{res}')
    

    hello egon my brother
    <function home at 0x000002932089A9D8> time is -1.0009005069732666
    res:egon

    1.5装饰器语法糖

    在被装饰函数正上方,并且是单独一行写上@装饰器名

    这样我们就可以不用写多余的代码直接调用被装饰对象的函数名就可以了

    import time
    
    
    def time_count(func):
        def wrapper(*args, **kwargs):
            start = time.time()
            res = func(*args, **kwargs)
            end = time.time()
            print(f'{func} time is {start - end}')
            return res
    
        return wrapper
    
    
    @time_count  # home = time_count(home)
    def home(name):
        print(f'hello  {name}  my brother')
        time.sleep(1)
        return name
    
    
    @time_count  # index = time_count(index)
    def index():
        print('hello word')
        time.sleep(1)
        return 123
    
    
    res = home('egon')
    print(f'res:{res}')
    

    hello egon my brother
    <function home at 0x000001EBDF02A9D8> time is -1.000500202178955
    res:egon

    1.6装饰器模板(很重要,要背)

    先定义有参函数,在有参函数内定义

    def deco(func):
        def wrapper(*args,**kwargs):
            res = func(*args,**kwargs)
            return res
        return wrapper
    

    二、有参装饰器

    无参装饰器只套了两层,本节将讲一个套三层的装饰器——有参装饰器,但现在我们先实现一个用户登录注册的装饰器。

    import time
    
    current_user = {'username': None}
    
    
    def login(func):
        # func = 最原始的index
        def wrapper(*args, **kwargs):
    
            if current_user['username']:
                res = func(*args, **kwargs)
                return res
            user = input('username:').strip()
            pwd = input('password:').strip()
            if user == 'nick' and pwd == '123':
                print('登录成功')
                current_user['user'] = user
                res = func(*args, **kwargs)
    
                return res
            else:
                print('失败了')
    
        return wrapper
    
    
    @login
    def home(name):
        print(f'hello  {name}  my brother')
        time.sleep(1)
        return name
    
    
    @login
    def index():
        print('hello word')
        time.sleep(1)
        return 123
    
    
    res = index()
    

    username:nick
    password:123
    登录成功
    hello word

    对于上面对的登陆注册,我们把用户登录成功的信息写入内存中。但是在工业上,用户的信息可以存在文本中、MySQL中、mongodb当中,但是我们只让用户信息来自于file的用户可以认证。因此我们可以改写上述的装饰器。

    import time
    
    current_user = {'username': None}
    
    
    def login(func):
        # func = 最原始的index
        def wrapper(*args, **kwargs):
    
            if current_user['username']:
                res = func(*args, **kwargs)
                return res
            user = input('username:').strip()
            pwd = input('password:').strip()
            engine = 'file'
            if engine == 'file':
                print('base of file')
                if user == 'nick' and pwd == '123':
                    print('登录成功')
                    current_user['user'] = user
                    res = func(*args, **kwargs)
    
                    return res
                else:
                    print('失败了')
            elif engine == 'mongodb':
                print('base of mongodb')
            else:
                print('default')
    
        return wrapper
    
    
    @login
    def home(name):
        print(f'hello  {name}  my brother')
        time.sleep(1)
    
    
    @login
    def index():
        print('hello word')
        time.sleep(1)
    
    
    res = index()
    

    username:nick
    password:123
    base of file
    登录成功
    hello word

    2.1三层闭包

    def f1(y):
    
        def f2():
            x = 1
    
            def f3():
                print(f"x: {x}")
                print(f"y: {y}")
            return f3
        return f2
    
    
    f2 = f1(2)
    f3 = f2()
    f3()
    

    x: 1
    y: 2

    现在需求改了,我们需要判断用户动态的获取用户密码的方式,如果是file类型的,我们则让用户进行认证。因此我们可以使用有参装饰器。

    import time
    
    current_uesr = {'username': None}
    
    
    def auth(engine='file'):
    
        def login(func):
            # func = 最原始的index
            def wrapper(*args, **kwargs):
    
                if current_user['username']:
                    res = func(*args, **kwargs)
    
                    return res
    
                user = input('username: ').strip()
                pwd = input('password: ').strip()
    
                if engine == 'file':
                    print('base of file')
                    if user == 'nick' and pwd == '123':
                        print('login successful')
                        current_uesr['usre'] = user
                        res = func(*args, **kwargs)
    
                        return res
                    else:
                        print('user or password error')
                elif engine == 'mysql':
                    print('base of mysql, please base of file')
                elif engine == 'mongodb':
                    print('base of mongodb, please base of file')
                else:
                    print('please base of file')
    
            return wrapper
    
        return login
    
    
    @auth(engine='mysql')
    def home(name):
        print(f"welcome {name} to home page")
        time.sleep(1)
    
    
    @auth(engine='file')
    def index():
        print('welcome to index')
        time.sleep(1)
    
    
    res = index()
    

    username: nick
    password: 123
    base of file
    login successful
    welcome to index

    由于两层的装饰器,参数必须得固定位func,但是三层的装饰器解除了这个限制。我们不仅仅可以使用上述单个参数的三层装饰器,多个参数的只需要在三层装饰器中多加入几个参数即可。也就是说装饰器三层即可,多加一层反倒无用。

  • 相关阅读:
    阿里云ECS7天训练营——05基于ECS搭建Java Web开发环境
    阿里云ECS7天训练营——04打造离线下载服务器
    阿里云ECS7天训练营——02搭建wiki知识库
    阿里云ECS7天训练营——03基于云服务器构建微信公众号管理系统
    阿里云ECS7天训练营——01打造专属云笔记
    Spring Cloud Config实现配置中心
    shell脚本条件判断if中-a到-z的意思
    Linux定时任务Crontab命令详解
    Linux下的tar压缩解压缩命令详解
    谈谈注册中心 zookeeper 和 eureka中的CP和 AP
  • 原文地址:https://www.cnblogs.com/Lin2396/p/11596194.html
Copyright © 2011-2022 走看看