闭包函数和装饰器

闭包函数和装饰器

一、闭包函数

1、简介

定义在函数内部的函数且内部的函数使用了外部函数名称空间中的名字(绑定数据的变量名)。两者必须要同时满足才能称为闭包函数。

1.形式一
def func():
    name = 'jason'
    def index():
        print(name)
    return index	
res = func()	# 获取返回值:变量名 = 函数()
print(res)  # <function func.<locals>.index at 0x00000258D6500840>
res()  # jason

2.形式二
# 在外层定义函数时传入参数,后续传实参
def func(name):
    def index():
        print(name)
    return index	
res = func('jason')	# 获取返回值:变量名 = 函数()
res()   # jason
"""func局部空间不会在调用结束后关闭,会等到所有的内部局部空间内的代码调用结束后关闭"""


'''
	1.函数嵌套
	2.内函数使用了外函数的变量名
	3.外函数返回了内函数的函数名
'''

2、实际应用

# 给函数体传参的方式
# 1.通过形参
def func(xxx):
    print(xxx)
    
# 2.闭包函数
def index():
    name = 'jason'
    def func():
        print(name)
    return func
res = index()
res()   # jason

def index1(name):
    # name = 'jason'
    def func1():
        print(name)
    return func1
res1 = index1('jason')
res1()  # jason
res2 = index1('oscar')
res2()	# oscar

2、实际应用

# 给函数体传参的方式
# 1.通过形参
def func(xxx):
    print(xxx)
    
# 2.闭包函数
def index():
    name = 'jason'
    def func():
        print(name)
    return func
res = index()
res()   # jason

def index1(name):
    # name = 'jason'
    def func1():
        print(name)
    return func1
res1 = index1('jason')
res1()  # jason
res2 = index1('oscar')
res2()	# oscar

image

二、装饰器

1、简介

本质:在不改变被装饰对象原来的调用方式和内部代码的情况下给被装饰的对象添加新的功能。

原则:对修改封闭,对扩展开发。就是不可以修改但是可以添加新的功能。起初听到我一脸懵,怎样才能不修改内部代码和调用的方式下添加新的功能,我有种做梦的感觉。

储备知识:

import time	# 导入时间模块

'''时间戳(记录秒数):当前时间距离1970年1月1日0时0分0秒所经历的秒数'''
# 一般用于统计代码运行的时间
print(time.time())

start_time = time.time()
for i in range(1000):
    print(i)
end_time = time.time()
print('for循环执行的时间秒数:%s'%(end_time - start_time))


'''让程序等待睡眠括号内时间'''
time.sleep(3)
print('今天这么热我居然还这么困')

2、推导流程

import time


def index():
    time.sleep(3)
    print('from index')

'''统计index函数执行的时间'''
start_time = time.time()
index()
end_time = time.time()
print(end_time - start_time)
^
^
^
def get_time():
    start_time = time.time()
    index()
    end_time = time.time()
    print('函数的执行时间是:',end_time - start_time)
# 缺陷:若有多个不同的函数需要统计时间,那么上述解决措施不够完善
# 解决:给函数体添加形参
^
^
^
def index():
    time.sleep(3)
    print('from index')


def home():
    time.sleep(6)
    print('from home')


def get_time(xxx):  # 传入形参
    start_time = time.time()
    xxx()
    end_time = time.time()
    print('函数的执行时间是:', end_time - start_time)
# get_time(index)
# get_time(home)
# # 缺陷:不同的形参个数的函数无法兼容统计
# # 解决:利用*args和**kwargs,但无法实现且执行方式改变
^
^
^
def func1(a):
    time.sleep(2)
    print('from func1')
get_time(func1)  # 报错,get_time函数体内xxx()括号内没有参数,与func1(a)不匹配

3、功能完善

# 1.方式一利用形参的形式传参,但是改变了调用方式
def index():
    time.sleep(3)
    print('from index')

def get_time(xxx):  # 传入形参
    start_time = time.time()
    xxx()
    end_time = time.time()
    print('函数的执行时间是:', end_time - start_time)

# 2.方式二利用闭包函数
def index():
    time.sleep(3)
    print('from index')


def outer(xxx):
    # xxx = index
    def get_time():  # 传入形参
        start_time = time.time()
        xxx()
        end_time = time.time()
        print('函数的执行时间是:', end_time - start_time)

    return get_time


# res = outer(index)  # res是变量名,可以是任意的名字(要符合命名规范)
# res()
index = outer(index)
index()

image

image

4、完整版本

import time

# 前期不足
def outer(xxx):
    def get_time():  # 传入形参
        start_time = time.time()
        xxx()
        end_time = time.time()
        print('函数的执行时间是:', end_time - start_time)
    return get_time

def index(name):
    time.sleep(1)
    print('from index')

def home():
    time.sleep(3)
    print('from home')

home = outer(home)
home()
index = outer(index)
index()     # 报错,需要传入参数,但是传入参数后是传给了get_time,get_time不需要参数还是会报错


# 后期完善
def outer(xxx):
    def get_time(*args, **kwargs):  # *与**在形参中的作用,args = ('jason',)
        start_time = time.time()
        xxx(*args, **kwargs)    # *和**在实参中的作用 func(*('jason',))
        end_time = time.time()
        print('函数的执行时间是:', end_time - start_time)
    return get_time

def index(name):
    time.sleep(1)
    print('from index')

def home():
    time.sleep(3)
    print('from home')

index = outer(index)
index('jason')
home = outer(home)
home()

def outer(xxx):
    def get_time(*args, **kwargs):  # *与**在形参中的作用,args = ('jason',)
        start_time = time.time()
        res = xxx(*args, **kwargs)    # *和**在实参中的作用 func(*('jason',))
        end_time = time.time()
        print('函数的执行时间是:', end_time - start_time)
        return res
    return get_time


def index(name):
    time.sleep(1)
    print('from index')

 
# 函数返回值
def outer(xxx):
    def get_time(*args, **kwargs):  # *与**在形参中的作用,args = ('jason',)
        start_time = time.time()
        res = xxx(*args, **kwargs)    # *和**在实参中的作用 func(*('jason',))
        end_time = time.time()
        print('函数的执行时间是:', end_time - start_time)
        return res
    return get_time

def index(name):
    time.sleep(1)
    print('from index')

def home():
    time.sleep(3)
    print('from home')
    return '执行home函数之后的返回值'

index = outer(index)
index('jason')
home = outer(home)
xxx = home()
print(xxx)

5、模板

def outer(func_name):
    def inner(*args, **kwargs):
        print('执行被装饰对象之前可以做的额外操作')
        res = func_name(*args, **kwargs)
        print('执行被装饰对象之后可以做的额外操作')
        return res

    return inner


eg:
def outer(func_name):
    def inner(*args, **kwargs):
        username = input('username:').strip()
        password = input('password:').strip()
        if username == 'jason' and password == '123':
            res = func_name(*args, **kwargs)
            return res
        else:
            print('权限不够')
    return inner


import time
def home():
    time.sleep(1)
    print('from home')
    return 'home返回值'

home = outer(home)
res = home()
print(res)

6、装饰器语法糖

def outer(func_name):
    def inner(*args, **kwargs):
        print('执行被装饰对象之前可以做的额外操作')
        res = func_name(*args, **kwargs)
        print('执行被装饰对象之后可以做的额外操作')
        return res

    return inner


import time
# 语法糖
@outer  # home = outer(真正的函数名home)
def home():
    time.sleep(1)
    print('from home')
    return 'home返回值'

print(home)
# 让装饰器做到以假乱真
from functools import wraps
def outer(func_name):
    @wraps(func_name)   # 仅仅让装饰器不容易被别人发现,做到真正的以假乱真
    def inner(*args, **kwargs):
        print('执行被装饰对象之前可以做的额外操作')
        res = func_name(*args, **kwargs)
        print('执行被装饰对象之后可以做的额外操作')
        return res

    return inner


import time
@outer  # home = outer(真正的函数名home)
def home():
    '''home函数,困困困'''
    time.sleep(1)
    print('from home')
    return 'home返回值'

help(home)
print(home)
home()

作业

1.编写一个用户认证装饰器
  基本要求
   执行每个函数的时候必须先校验身份 eg: jason 123
  拔高练习(有点难度)
   执行被装饰的函数 只要有一次认证成功 那么后续的校验都通过
     函数:register login transfer withdraw 
   提示:全局变量 记录当前用户是否认证

data_dict = {}

def register():
    while True:
        name = input('请输入注册的用户名:')
        if name in data_dict:
            print('已注册')
            continue
        emp_name = input('请输入你的用户名:').strip()
        emp_pwd = input('请输入你的密码:').strip()
        emp_money = input('请输入你的金额:').strip()
        # 构造账户信息字典
        acc_dict = {}
        acc_dict['姓名'] = emp_name
        acc_dict['密码'] = emp_pwd
        acc_dict['余额'] = emp_money
        data_dict[name] = acc_dict
        break

def login():
    while True:
        name = input('请输入登录的用户名:').strip()
        if name not in data_dict:
            print('该用户不存在')
            continue
        pwd = input('请输入登录密码:').strip()
        emp_data = data_dict.get(name)
        real_name = emp_data['姓名']
        real_pwd = emp_data['密码']
        if real_name == name and real_pwd == pwd:
            print('登录成功')
            break
        else:
            print('登录失败')
            break

def withdraw():
    while True:
        name = input('请输入你的用户名:').strip()
        emp_data = data_dict.get(name)
        money = input('请输入要提现的金额:').strip()
        money = int(money)
        real_money = emp_data['余额']
        real_money = int(real_money)
        new_money = real_money - money
        emp_data['余额'] = new_money
        data_dict[name] = emp_data
        print(f'账户余额为:{new_money}')
        print('提现成功')
        break

def transfer():
    while True:
        name = input('请输入你的用户名:').strip()
        emp_data = data_dict.get(name)
        w_money = input('请输入要转账的金额:').strip()
        w_money = int(w_money)
        y_money = emp_data['余额']
        y_money = int(y_money)
        n_money = y_money - w_money
        emp_data['余额'] = n_money
        data_dict[name] = emp_data
        print(f'账户余额为:{n_money}')
        print('转账成功')
        break



# 提前构造功能字典
func_dict = {'1': register,
             '2': login,
             '3': withdraw,
             '4': transfer
             }

while True:
    print("""
    1.注册功能
    2.登录功能
    3.提现功能
    4.转账功能
    """)
    choice = input('>>>:').strip()
    if choice in func_dict:
        func_name = func_dict.get(choice)
        func_name()
    else:
        print('没有该功能编号')
posted @ 2022-07-05 22:39  努力努力再努力~W  阅读(76)  评论(0)    收藏  举报