闭包函数与装饰器 一、闭包函数 二、装饰器 三、三层装饰器

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

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

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

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

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)