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()
原文地址:https://www.cnblogs.com/jiangjunwang/p/13396142.html