装饰器

    今日内容

  • 闭包函数的简介
  • 闭包函数的应用
  • 装饰器的简介
    •   无参装饰器
    •        有参装饰器
    •        具有返回值的装饰器
  • 语法糖
  • 装饰器模板
  • 作业

闭包函数的简介

当一个函数需要传参时我们可以直接用形参传值

现在闭包函数也可以当做传参的一种形式

闭包函数要满足两种特征

  1. 定义在函数内部的函数
  2. 内部函数使用了外部函数的名称空间的名字

只有满足以上两种条件才能被称之为闭包函数

username = 'tony'
def func():    
    def inner():
        print(username)
    inner()
func()
'''
像这种情况inner函数虽然在func函数中
但是inner中的名字不是在func中找到的是在全局名称空间中找到的
所以inner不是闭包函数
'''

def func():    
    username = 'tony'
    def inner():
        print(username)
    inner()
func()
'''
像inner函数时在func函数里的并且inner中的变量名的值是用到了func中得到名字
所以inner就可以被称为闭包函数
'''

闭包函数的应用

def func():
    username = 'tony'

    def inner():
        print(username)

    inner()

func()
'''
想inner函数时在func函数里的并且inner中的变量名的值是用到了func中得到名字
所以inner就可以被称为闭包函数
但是这样传参就只能打印'tony'给写死了所以需要改动下
'''
def func(username):
    def inner():
        print(username)
        pass
    return inner
res = func('tony')  # tony
res()
res = func('kevin')  # kevin
res()
# 这样就能把传参写活了用户想传啥传啥

 

  

装饰器的简介

装饰器就是在不改变装饰对象的调用方式函数体代码的前提下给被装饰器对象添加新功能

eg:

# 比如现在我们编写了一个函数
def func():
    print('from func')
func()

# 现在我们在执行这个函数时我们需要计算这个函数执行了多长时间

这里我们需要用到一个新知识time模块

你可以把模块理解为一些大佬编写好的一些功能  我们只要调用即可

import time

print(time.time())  # 1657007705.1425676
'''时间戳(秒数):这串数字是从1970年1月1日0时0分0秒到刚刚运行代码的时候的距离'''
# eg:
star_time = time.time()
for i in range(10000):
    print(i)
stop_time = time.time()
print(stop_time - star_time)  # 0.044882774353027344
# 这样就能计算出for循环打印完全部的值需要多少时间
time.sleep(3)  # 这行代码的意思就是先停三秒再执行
print('延迟三秒再执行')

现在我们需要正式对func装饰

1.无参装饰器

import time


def func():
    time.sleep(1)
    print('from func')
'''
既然要计算给函数func添加功能计算运行时间
那么我们可以在调用函数前面获取一个时间戳后面也获取一个时间戳
然后两个时间戳一减即可
'''
start = time.time()
func()
stop = time.time()
print(stop - start)  # 1.0003433227539062
'''
这样虽然说计算出了func函数运行的时间但是存在缺陷
如果py文件中存在多个函数需要计算运行时间
那么就会重复编写代码 编写代码效率太低
我们可以把它封装成函数
'''
def get_time():
    start = time.time()
    func()
    stop = time.time()
    print(stop - start)
get_time()  # 1.0008282661437988
'''
这样也能计算出func的运行时间但是还是有缺陷
因为现在我们把get_time函数的功能写死了只能计算func的运行时间
没法计算其他函数的运行时间
我们可以把func当做变量名 用闭包函数包给它
'''
def home():
    time.sleep(2)
    print('from home')

def outter(func):
    def get_time():
        start = time.time()
        func()
        stop = time.time()
        print(stop - start)
    return get_time
func = outter(func)
func()  # 1.0003347396850586
home = outter(home)
home()  # 2.000154972076416
'''
这样我们就能通过闭包函数动态传参
'''

2.有参装饰器

以上装饰器都是对被装饰函数没有参数的时候的装饰

如果被装饰函数有参数时就又要加东西

# 有参装饰器
import time


# 比如现在func有两个形参
def func(a, b):
    time.sleep(1)
    print('from func')

def outter(func):
    def get_time():
        start = time.time()
        func()
        stop = time.time()
        print(stop - start)
    return get_time
func = outter(func)
func()  # 这样会直接报错 因为func有两个形参 那么我们实参就必须要个实参才行
# 怎么解决呢?
# 其实直接在装饰器中添加两个参数即可
def outter(func):
    def get_time(a,b):
        start = time.time()
        func(a,b)
        stop = time.time()
        print(stop - start)
    return get_time
func = outter(func)
func(1,2)  # 1.0008280277252197
'''
这样就能计算出func的运行时间 但是这样还是存在着缺陷
因为传参被我们写死了 只能传两个位置实参 
所以我们要在形参和实参中都要用到*和**号
'''
def outter(func):
    def get_time(*args, **kwargs):
        start = time.time()
        func(*args, **kwargs)
        stop = time.time()
        print(stop - start)
    return get_time
func = outter(func)
func(1, 2)  # 1.0008559226989746
'''
这样被装饰对象不管形参是什么样都可以
这样装饰器基本完成
'''

3.返回值

有时候被装饰器对象是有返回值的

那么我们装饰器就需要添加值了

import time
# 当被装饰器对象有返回值时
def func(a, b):
    time.sleep(1)
    print('from func')
    return 'func的返回值'
res = func(1,2)
print(res)  # func的返回值
'''
一般我们获取函数得返回值时就是把函数赋值给一个变量名 
那么这个变量名就会接收这个函数的返回值了
而现在我们的装饰器是无法接收被装饰器返回值的
'''
def outter(func):
    def get_time(*args, **kwargs):
        start = time.time()
        func(*args, **kwargs)
        stop = time.time()
        print(stop - start)
    return get_time
func = outter(func)
print(func)  # <function outter.<locals>.get_time at 0x000001FFEB00F158>
'''
现在的返回值时get_time的内存地址
现在我们需要一个变量名赋值给被装饰器的函数然后在返回
'''
def outter(func):
    def get_time(*args, **kwargs):
        start = time.time()
        res = func(*args, **kwargs)
        stop = time.time()
        print(stop - start)
        return res  # 这样就能把传进来的函数名的返回值返回回去了
    return get_time
func = outter(func)  # 1.0003952980041504
res = func(1,2)
print(res)  # func的返回值
'''
这样就能把被装饰器的返回值原封不动的返回回去
'''

 

 

一般写到着装饰器已经很完美了,使用者已经很难发现被装饰器对象到底有没有被装饰过

如果被装饰器对象还有注释的话还是能被一些大佬发现的所以还可以改变一丢丢

import time
# 当被装饰器对象有返回值时
def func(a, b):
    ''' func的注释'''
    time.sleep(1)
    print('from func')
    return 'func的返回值'
# 我们可以使用help关键字查看func的注释
help(func)  # func的注释
# 而我们如果想让装饰器返回被装饰器对象的注释的话需要 functools模块
def outter(func):
    def get_time(*args, **kwargs):
        '''get_time的注释'''
        start = time.time()
        res = func(*args, **kwargs)
        stop = time.time()
        print(stop - start)
        return res 
    return get_time
func = outter(func)
help(func)  # get_time的注释

# 现在返回的get_time的注释 我们是需要在get_time函数上面加上functools模块即可 from functools import wraps def outter(func): @wraps(func) def get_time(*args, **kwargs): '''get_time的注释''' start = time.time() res = func(*args, **kwargs) stop = time.time() print(stop - start) return res return get_time func = outter(func) help(func) # func的注释 # 这个时候的装饰器已经很完美了 不容易被大佬们发现是被装饰过的了

语法糖

from functools import wraps
import time

def outer(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        start = time.time()
        res = func(*args, **kwargs)
        stop = time.time()
        print(stop - start)
        return res

    return wrapper

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

index = outer(index)
index()
'''
其实这样的装饰器已经很完美了  但是如果被装饰对象变多的时候我们可以把这两行简化成一行
'''


def outer(func):
    @wraps(func)  # 可写可不写 主要是不容易被发现是否被装饰过
    def wrapper(*args, **kwargs):
        start = time.time()
        res = func(*args, **kwargs)
        stop = time.time()
        print(stop - start)
        return res

    return wrapper
@outer
def index():
    '''index的注释'''
    time.sleep(1)
    print('from index')
    return 'inedx的返回值'

res = index()  # from index  1.0003662109375
print(res)  # inedx的返回值
help(index)  #   index的注释
'''
这样我们只需要在被装饰器对象上面一行加一个@装饰器名字 即可
@outer的意思就是把它下面一行的函数名拿来 然后进行  被装饰对象名字 = 装饰器名字(被装饰器对象名字)  操作
比如上方  index = outer(index)
这样就不用我们自己写了
'''

 

 

装饰的模板

from functools import wraps


def outer(func):
    @wraps(func)  # 可写可不写 主要是不容易被发现是否被装饰过
    def wrapper(*args, **kwargs):
        res = func(*args, **kwargs)
        return res

    return wrapper

'''
这样我们就能在res = func(*args, **kwargs)前面或则后面添加新功能了
然后在被装饰对象上面写上语法糖即可
'''

作业

  • 利用装饰器对前面的作业添加用户认证功能
user_data_dict = {}

is_login = {
    'username': None
}

'''我们可以在全局变量编写一个字典通过用户名判断用户是否登入'''
def auth_login(func):
    def wrapper(*rags, **kwargs):
        if is_login.get('username'):  # 如果没有登入'username'的值就是None
            res = func(*rags, **kwargs)
            return res
        else:
            print('你还未登入,请先登入')
            login()

    return wrapper


@auth_login
def register():
    while True:
        emp_id = input('请输入员工编号,输入q退出>>>:').strip()
        if emp_id == 'q':
            break
        # 2.判断员工编号是否已存在
        if emp_id in user_data_dict:
            print('员工编号已存在,无法添加')
            continue
        emp_name = input('请输入员工姓名>>>:').strip()
        emp_age = input('请输入员工年龄>>>:').strip()
        emp_post = input('请输入员工岗位>>>:').strip()
        emp_salary = input('请输入员工薪资>>>:').strip()
        # 3.创建一个小字典
        data_dict = {'emp_id': emp_id, 'emp_name': emp_name, 'emp_age': emp_age, 'emp_post': emp_post,
                     'emp_salary': emp_salary}
        # 4.存入到大字典
        user_data_dict[emp_id] = data_dict
        # 5.提示信息
        print(f'员工{emp_name}添加成功')


def login():
    while True:
        username = input('请输入你的用户名>>>:').strip()
        password = input('请输入你的密码>>>:').strip()
        if username == 'jason' and password == '123':
            print('登入成功')
            is_login['username'] = username  # 如果登入了就把字典中的值改成登入的用户名
            break
        else:
            print('登入失败')
            continue


@auth_login
def amend_salary():
    target_id = input('请输入员工编号>>>:').strip()
    if target_id not in user_data_dict:
        print('员工编号不存在,无法修改')
        return
    # 提取改员工的字典
    user_dict = user_data_dict[target_id]
    # 获取新的薪资
    new_salary = input('请输入该员工新的薪资>>>:').strip()
    # 修改该员工的薪资
    user_dict['emp_salary'] = new_salary
    # 修改大字典
    user_data_dict[target_id] = user_dict
    # 提示信息
    print(f'员工编号{target_id}的薪资已修改为{new_salary}')


@auth_login
def check_all():
    if len(user_data_dict) == 0:
        print('没有员工信息')
    else:
        for staff in user_data_dict.values():
            print('staff info'.center(30, '*'))
            print(f"""
    员工编号:{staff.get('emp_id')}
    员工姓名:{staff.get('emp_name')}
    员工年龄:{staff.get('emp_age')}
    员工岗位:{staff.get('emp_post')}
    员工薪资:{staff.get('emp_salary')}
            """)
            print('end'.center(30, '*'))


@auth_login
def check_assig():
    target_id = input('请输入员工编号>>>:').strip()
    # 判断员工编号是否存在
    if target_id not in user_data_dict:
        print('员工编号不存在,无法查看')
        return
    # 提取改员工的字典
    user_dict = user_data_dict[target_id]
    # 可以for循环也可以解压赋值
    # emp_id, emp_name, emp_age, emp_post, emp_salary = user_dict.values()
    print('staff info'.center(30, '*'))
    print(f"""
              员工编号:{user_dict.get('emp_id')}
              员工姓名:{user_dict.get('emp_name')}
              员工年龄:{user_dict.get('emp_age')}
              员工岗位:{user_dict.get('emp_post')}
              员工薪资:{user_dict.get('emp_salary')}
                      """)
    print('end'.center(30, '*'))


@auth_login
def delete_staff():
    delete_id = input('请输入员工编号>>>:').strip()
    # 判断员工编号是否存在
    if delete_id not in user_data_dict:
        print('员工编号不存在,无法删除')
        return  # 删除
    res = user_data_dict.pop(delete_id)
    print(f'员工编号{delete_id}已删除', res)


def is_choice(choice):
    if len(choice) == 0:
        print('请输入正确功能编号')
        return True
    if not choice.isdigit():
        print('请输入数字')
        return True


func_dict = {
    '0': ['退出', None],
    '1': ['注册', register],
    '2': ['登入', login],
    '3': ['修改薪资', amend_salary],
    '4': ['查看指定员工', check_assig],
    '5': ['查看所有员工', check_all],
    '6': ['删除员工', delete_staff]
}

while True:
    for func in func_dict:
        print(func, func_dict[func][0])
    choice = input('请输入功能编号>>>:').strip()
    if is_choice(choice):
        continue
    elif choice == '0':
        print('欢迎下次继续')
        break
    elif choice in func_dict:
        func_dict[choice][1]()
    else:
        print('没有功能编号')

 

posted @ 2022-07-05 17:32  stephen_hao  阅读(26)  评论(0)    收藏  举报