zoukankan      html  css  js  c++  java
  • python 闭包和装饰器

    一、闭包

    定义:内层函数对外层函数非全局变量的引用,就会形成闭包,闭包只存在于嵌套函数中

    • 被引用的非全局变量也称为自由变量,这个自由变量会与内层函数产生一个绑定关系
    • 自由变量不会再内存中消失
    • 闭包的作用,保证数据的安全 (返回的函数对象,不仅仅是一个函数对象,在该函数外还包裹了一层作用域,这使得,该函数无论在何处调用,优先使用自己外层包裹的作用域)
    # 默认情况下inner函数是没有办法被外层执行的
    def outer():
        name = "小白,自学编程"
        def inner():
            print("inner",name)
        return inner #返回inner的内存地址
    func = outer() #相当于把inner赋值给func
    func() #相当于执行了inner
    
    print(outer.__code__.co_freevars) # 查看有没有自由变量,如果有说明是闭包
    

    二、装饰器

    定义:在不改变源代码和调用方式的情况下增加新功能,装饰器的本质就是闭包

    准守开放封闭原则

    封闭:已经实现的模块代码不应该被修改

    开放:对现有功能的扩展开放

    2.1、初识别装饰器

    写一个函数,测试另外同事写的函数执行效率

    版本一

    import time
    
    def index():
        time.sleep(2)
        print('欢迎访问博客园主页')
        return 666
    
    
    def timer(func):
        def inner():
            start_time = time.time()
            func()
            stop_time = time.time()
            print(f'此函数的执行效率为{stop_time-start_time}')
        return inner #返回inner
    
    
    index = timer(index)
    index() #相当于执行inner
    

    通过以上的代码已经实现了测试执行效率的功能,但是有两个问题,

    • 第一,当执行index() 的时候其实是执行了inner,而inner函数里边的func其实才是真正执行的index(),所以原来index里边的return 666 无法返回,如果想要返回应该把func()的执行结果赋值给一个变量,然后inner函数return这个变量
    • 第二,如果执行index() 需要带有变量,那么这个时候会报错,原因是执行index()相当于执行了 inner(),而执行inner()又相当于执行func(),所以如果想要index()带有变量,inner()和func()都应该加上相应的变量,可用使用万能变量,接收任何参数

    版本二:

    import time
    
    def index(name):
        time.sleep(2)
        print(f'{name}欢迎访问博客园主页')
        return 666
    
    
    def timer(func):
        def inner(*args,**kwargs):
            start_time = time.time() 
            s = func(*args,**kwargs) # 将func的返回结果赋值给一个变量
            stop_time = time.time()
            print(f'此函数的执行效率为{stop_time-start_time}')
            return s # 返回func的返回结果,相当于返回index的返回结果
        return inner
    
    index = timer(index)
    index('老王')
    
    

    通过版本二的改进,上边说的两个问题已经解决了,但是还有一个问题,和一个改进的地方

    • 第一,每次函数在添加装饰器的时候需要在调用函数之前加上一句index = timer(index) ,显得比较麻烦,python引入了一个语法糖的东西,改进这一缺点
    • 第二,在使用装饰器之后,原函数的属性会变成包装器的属性,如果还想保留原函数的属性就需要用functools.wraps functools.wraps 就是装饰包装器的装饰器

    版本三

    from functools import wraps #调用装饰包装器模块
    import time
    
    def timer(func):
        @wraps(func) # 装饰包装器
        def inner(*args,**kwargs): #包装器
            start_time = time.time()
            s = func(*args,**kwargs)
            stop_time = time.time()
            print(f'此函数的执行效率为{stop_time-start_time}')
            return s
        return inner
    
    @timer  # 语法糖 相当于index = timer(index)
    def index(name):
        time.sleep(2)
        print(f'{name}欢迎访问博客园主页')
        return 666
    
    
    index('老王')
    

    2.2 、装饰器模板

    def wrapper(f):
        def inner(*args,**kwargs):
            '''添加额外功能,执行被装饰函数之前的操作'''
            ret = f(*args,**kwargs)
            '''添加额外功能,执行被装饰函数之后的操作'''
            return ret
        return inner
    

    三、装饰器的应用

    网页登录验证

    某网站需要给所有页面添加用户名密码验证功能,只有通过验证的用户才可以访问网页资源,并且在一个网页通过验证后,其他网页可直接访问,如果用户密码输入错误超过三次直接退出程序

    from functools import wraps
    account={
        "is_authenticated":False,#用户登录了就把这个改成Ture
        "username": "laowang", #假装这里是数据库的用户
        "password": "abc123"
    }
    
    def login(funk):
        @wraps(funk)
        def inner(*args):
            if account["is_authenticated"] is False:
                count = 0
                while count < 3:
                    username = input("user:")
                    password = input("password:")
                    if username == account["username"] and password == account["password"]:
                        print("welcome login.....")
                        account["is_authenticated"]=True
                        funk(*args)
                        break
                    else:
                        print("wrong username or password")
                    count += 1
                    if count == 3:
                        exit()
            else:
                print("用户以登录,验证通过....")
                funk(*args)
        return inner #返回inner的内存地址
    
    @login
    def home():
        print("欢迎来到首页")
    @login
    def america():
        print("欢迎来到欧美专区")
    
    @login #添加装饰器,等于henan = login(henan)
    def henan(vip_live):
        if vip_live > 3:  #vip等及大于3的解锁本专区高级私密
            print("欢迎来到河南专区,解锁本专区高级私密")
        else:
            print("欢迎来到河南专区")
    
    # henan = login(henan)
    
    home()
    henan(4)
    america()
    
  • 相关阅读:
    Python超级篇-机器学习
    python高给篇-爬虫
    机器学习之-sklearn
    luogu3384 【模板】树链剖分
    bzoj3884 上帝与集合的正确用法
    luogu2765 魔术球问题
    poj2976 Dropping tests 01分数规划
    luogu2764 最小路径覆盖问题
    luogu2763 试题库问题
    luogu2762 太空飞行计划问题
  • 原文地址:https://www.cnblogs.com/jiangjunwang/p/13396142.html
Copyright © 2011-2022 走看看