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

    一、闭包函数

    闭是封闭(函数内部的函数),包是包含(该内部函数对外部作用域而非全局作用域的变量的引用)。

    闭包就是指:函数内部的函数 对外部作用域 而非全局作用域 的引用。

    闭包函数的作用:可以把 闭包函数内部的变量 + 闭包函数内部的函数 这两者包裹在一起, 然后通过返回值的形式返回出来。

    闭包函数的特点:返回的函数对象,不仅仅是一个函数对象,在该函数外还包裹了一层作用域,这使得,该函数无论在何处调用,优先使用自己外层包裹的作用域。

    def f1(url):
        def f2():
            print(url)
            
        return f2
    
    res = f1()  # res = f2
    res()  # res() == f2()
    

    二、装饰器

    1、装饰器的定义

    装饰器:就是一种用来 为 被装饰函数对象 添加额外功能的 函数

    特点:

    1. 不改变原函数的代码
    2. 不改变原函数的调用方式
    import time
    
    def index():
        print('hello world')
        time.sleep(1)
    
    def f2():
        print('f2')
        time.sleep(2)
    
    
    start = time.time()
    index()
    end = time.time()
    print(f'run time is {end-start}')
    
    
    start = time.time()
    f2()
    end = time.time()
    print(f'run time is {end-start}')
    
    
    

    index 和 f2 的功能一样,而且可以发现对他们的使用方式也是一样的,因此可以想办法将调用它们的代码简化,即可以再定义一个函数来使用它们。

    第一种方法:改变调用方式

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

    可以发现,这样做和上面一种方法得到的结果是一样的,但是可以发现使用index的方法不一样了。

    第二种方法:包给函数-外包

    import time
    
    def index():
        print('hello world')
        time.sleep(1)
    
    def time_count(func):
        def wrapper():
            start = time.time()
            func()
            end = time.time()
            print(f'{func} run time is {end-start}')
        return wrapper
    
    # f = time_count(index)
    # f()  # 这里的f其实就是在调用index函数,但如果命名为f,用户就不知道你在调用index
    
    index = time_count(index)
    index()  # 新变量也命名为index,对用户来说,就是在调用之前的index,只不过功能更新了
    
    

    没有改变index的源代码,也没有改变index函数的调用方式,这里的time_count函数就是装饰器。

    我们发现,最后调用index函数的时候实际上就是在调用wrapper函数,因此我们想到,如果index有返回值,那wrapper函数中必须要有一个变量用来接收index函数的返回值,并作为wrapper函数的返回值返回出来。

    我们做了如下调整:

    import time
    
    def index():
        print('hello world')
        time.sleep(1)
    
        return 123
    
    def time_count(func):
        def wrapper():
            start = time.time()
            res = func()
            end = time.time()
            print(f'{func} run time is {end-start}')
            return res
        return wrapper
    
    
    index = time_count(index)
    res = index()
    print(f'res:{res}')
    

    最后可以返回index函数的返回值 123。

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

    import time
    
    def home(name):
        print(f'welcome {name} to home page')
        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 {end - start}')
    
            return res
        return wrapper
    
    home = time_count(home)  # 前一个home = wrapper , 括号里的home = home函数
    
    res =  home('egon')  # == wrapper('egon')
    print(f'res:{res}')
    
    

    2、装饰器语法糖

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

    import time
    
    def time_count(func):
        def wrapper(*args,**kwargs):
            start = time.time()
            res = func(*args,**kwargs)
            end = time.time()
            print(f'{func} run time is {end-start}')
    
            return res
        return  wrapper
    
    @time_count
    def home(name):
        print(f'welcome {name} to home page')
        time.sleep(1)
    
        return name
    
    res = home('egon')
    print(f'res:{res}')
    

    效果和上面一个方法一样

    3、装饰器模板

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

    按模板写就行了

    三、三层装饰器

    套三层的装饰器

    username_list = []
    
    
    def sanceng(role):
        def login_deco(func):
            def wrapper(*args,**kwargs):
    
                if username_list:
                    print('请勿重复登录')
                    res = func(*args,**kwargs)
                    return res
    
                username_lnp = input('请输入用户名:')
                pwd_inp = input('请输入密码:')
    
                with open(f'{role}_info.txt','r',encoding='utf8') as fr:
                    for user_info in fr:
                        username,pwd = user_info.strip().split(':')
                        if username_lnp == username and pwd_inp== pwd:
                            print('登录成功')
                            username_list.append(username)
    
                            res = func(*args,**kwargs)
                            return res
    
                    else:
                        print('登陆失败')
    
            return wrapper
    
        return login_deco
    
    
    @sanceng('user')
    def index(x,y):
        print('index')
        print('x,y',x,y)
    
        return  123
    
    res = index(10,20)
    
  • 相关阅读:
    我常用到的前端工具
    三级菜单
    voxel安装2
    voxel安装
    Linux 安装 nginx
    pip DEPRECATION: The default format will switch to columns”错误
    Python升级安装的一些问题
    Linux下修改Apache即httpd的端口
    Linux中yum命令镜像源和出错解决方案
    Linux 别名 Apache
  • 原文地址:https://www.cnblogs.com/bowendown/p/11558926.html
Copyright © 2011-2022 走看看