闭包函数
闭是封闭(函数内部的函数),包是包含(该内部函数对外部作用域而非全局作用域的变量的引用)。
闭包就是指:函数内部的函数 对外部作用域 而非全局作用域 的引用。
闭包函数的作用:可以把 闭包函数内部的变量 + 闭包函数内部的函数 这两者包裹在一起, 然后通过返回值的形式返回出来。
闭包函数的特点:返回的函数对象,不仅仅是一个函数对象,在该函数外还包裹了一层作用域,这使得,该函数无论在何处调用,优先使用自己外层包裹的作用域。
def f1(url):
    def f2():
        print(url)
        
    return f2
res = f1()  # res = f2
res()  # res() == f2()
装饰器
装饰器:就是一种用来 为 被装饰函数对象 添加额外功能的 函数
特点:
- 不改变原函数的代码
- 不改变原函数的调用方式
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}')
装饰器语法糖
在被装饰函数正上方,并且是单独一行写上@装饰器名
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}')
效果和上面一个方法一样
装饰器模板
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)
迭代器引入
可迭代对象
含有__iter__方法的就叫做可迭代对象
迭代器
含有__iter__和__next__方法的就叫做迭代器
生成器
含有yield关键字的函数就叫做生成器