4.5 Python3 进阶 - 闭包 & 装饰器

>>返回主目录


源码

# 闭包应用:计数器
def counter():
    numbers = [0]

    def add_one():
        numbers[0] += 1
        print(f'第{numbers[0]}次调用')
    return add_one


count1 = counter()  # 调用外函数
count1()  # 调用count1的闭包函数
count1()
count2 = counter()()  # 直接调用外函数的闭包函数
count1()

源码

# 闭包函数,同级之间可以互相进行访问,例如:
def out_fun(num1, num2):
    num3 = 10

    def inner_fun_1(num4):
        num5 = 20
        sum_nums = num1 + num2 + num3 + num4 + num5
        print('所有数字相加后的结果是:', sum_nums)

    def inner_fun_2(num):
        inner_fun_1(num)
    return inner_fun_2


sum_total_1 = out_fun(1, 2)  # 调用out_fun装饰器,返回inner_fun_2赋值给变量sum_total_1
sum_total_2 = out_fun(10, 20)  # 调用out_fun装饰器,返回inner_fun_2赋值给变量sum_total_2
sum_total_1(3)
sum_total_1(4)
sum_total_1(5)
sum_total_2(30)  # sum_total_1和sum_total_2内存地址相互独立
sum_total_2(40)



源码

import time


# 装饰器定义
# 简单装饰器格式,传入func为函数
def decorator_1(func):
    def wrapper():
        start_time = time.time()
        func()
        end_time = time.time()
        print(f'原函数执行速度是:{end_time - start_time}')
    return wrapper


# 装饰器调用
@decorator_1  # 使用@语法糖,调用装饰器
def func_1():
    print('func_1 为原函数')
    time.sleep(1)


func_1()
# 使用普通方式调用
# call_func = decorator_1(func_1)
# call_func()

源码

# 万能装饰器
def decorator_2(func):
    def wrapper(*args, **kwargs):
        start_time = time.time()
        func(*args, **kwargs)
        end_time = time.time()
        print(f'原函数执行速度是:{end_time - start_time}')
    return wrapper


@decorator_2
def func_2(name, courses, date_time='2021-06-09'):
    courses_str = ''
    for i in courses:
        courses_str = courses_str + i + ','
    print(f'我是{name},今天是{date_time},学习了:{courses_str}的课程')
    time.sleep(1)


func_2('Portos', ['Python', '函数', 'decorator'], date_time='2021-06-10')


源码

# 带参数装饰器
def decorator_with_para(*para_list, **para_dict):
    def decorator_3(func):
        def wrapper(*args, **kwargs):
            print('传入的para_list可供内部装饰器使用:', para_list)
            print('传入的para_dict可供内部装饰器使用:', para_dict)
            start_time = time.time()
            func(*args, **kwargs)
            end_time = time.time()
            print(f'原函数执行速度是:{end_time - start_time}')
        return wrapper
    return decorator_3


@decorator_with_para('可变参数', para_dict_data='关键字参数')
def func_3(name, courses, date_time='2021-06-09'):
    courses_str = ''
    for i in courses:
        courses_str = courses_str + i + ','
    print(f'我是{name},今天是{date_time},学习了:{courses_str}的课程')
    time.sleep(1)


func_3('Portos3', ['Python3', '函数', 'decorator'], date_time='2021-06-10')

源码

# 复合/多层装饰器调用
# 第一个装饰器
def decorate_4(func):
    def wrapper(*args, **kwargs):
        start_time = time.time()
        func(*args, **kwargs)
        end_time = time.time()
        print(f'原函数执行速度是:{end_time - start_time}')
    return wrapper


# 第二个装饰器
def decorate_5(func):
    def wrapper(*args, **kwargs):
        func(*args, **kwargs)
        print(f'此处调用了装饰器decorate_5')
    return wrapper


# 第一个装饰器和第二个装饰器都装饰同一个函数
@decorate_5  # 后装饰的,先调用
@decorate_4  # 先装饰的,后调用
def func_4(name, courses, date_time='2021-06-09'):
    courses_str = ''
    for i in courses:
        courses_str = courses_str + i + ','
    print(f'我是{name},今天是{date_time},学习了:{courses_str}的课程')
    time.sleep(1)


func_4('Portos4', ['Python4', '函数', 'decorate'], date_time='2021-06-10')

源码

# Author:PortosHan
# Datetime:2021/6/17 21:00
# Project:zbcf_python_lesson_project
# 装饰器调用练习(场景:权限验证)
"""
付款时校验登录权限,练习:
第一步:调用付款函数进行付款;
第二步:校验是否登录;
第三步:没有登录,重新登录后再付款;
第四步:登录成功后,重新调用付款函数进行付款。
"""
import time

is_login = False  # 默认没有登录


# 定义一个登录函数,判断是否登录成功
def login():
    user_name = input('请输入用户名:')
    pwd = input('请输入密码:')
    if user_name == 'portos'.lower() and pwd == '123456':
        return True
    else:
        return False


# 定义一个装饰器,判断是否登录
def login_decorator(func):
    def wrapper(*args, **kwargs):
        print('------校验登录状态------')
        global is_login
        if is_login:
            func(*args, **kwargs)
        else:
            print('用户没有登录,不能付款,3s后自动跳转到登录页面!')
            time.sleep(3)
            # 登录成功/失败后,重新赋值全局变量的值
            is_login = login()
            # 登录成功后,重新调用当前付款函数
            func(*args, **kwargs)
    return wrapper


# 调用付款函数时,使用装饰器校验是否登录
@login_decorator
def pay(money):
    print(f'------付款金额:{money}元------')
    print('付款中')
    time.sleep(3)
    print('------付款成功!------')


pay(1000)
# pay(100)

源码

# 装饰器:批量取消装饰器(直接调用原函数)
def decorate_with_para_7(para_flag):
    def decorate_7(func):
        def wrapper(*args, **kwargs):
            if para_flag:
                func(*args, **kwargs)
                print('此处是被装饰过的原函数!')
            else:
                return func(*args, **kwargs)

        return wrapper

    return decorate_7


@decorate_with_para_7(False)
def func_7():
    print('1此处是原函数')


func_7()

源码

from functools import wraps


# wraps装饰器:取消装饰器(直接调用原函数)
def decorate_8(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        """被wraps装饰过的函数"""
        func(*args, **kwargs)
        print(f'此处是被装饰过的原函数!现在函数名是:{wrapper.__name__},文档说明是:{wrapper.__doc__}')

    return wrapper


@decorate_8
def func_8():
    """原函数"""
    print('2此处是原函数')


# func_8()
func_8.__wrapped__()  # 有wraps装饰器时,访问原函数


源码

# 例如:如下两个(或更多)装饰器,装饰同一个函数
def decorate_9(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        """1被wraps装饰过的函数"""
        func(*args, **kwargs)
        print(f'1此处是被装饰过的原函数!现在函数名是:{wrapper.__name__},文档说明是:{wrapper.__doc__}')

    return wrapper


def decorate_10(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        """2被wraps装饰过的函数"""
        func(*args, **kwargs)
        print(f'2此处是被装饰过的原函数!现在函数名是:{wrapper.__name__},文档说明是:{wrapper.__doc__}')

    return wrapper


def decorate_11(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        """3被wraps装饰过的函数"""
        func(*args, **kwargs)
        print(f'3此处是被装饰过的原函数!现在函数名是:{wrapper.__name__},文档说明是:{wrapper.__doc__}')

    return wrapper


@decorate_11
@decorate_10
@decorate_9
def func_9():
    """原函数"""
    print('3此处是原函数')


func_9.__wrapped__()  # 此时只会略过最外层的包装层decorate_11
func_9.__wrapped__.__wrapped__.__wrapped__()  # 若想略过其他层,需继续调用其他层的__wrapped__属性

>>返回主目录

posted @ 2021-06-18 12:05  PortosHan  阅读(52)  评论(0编辑  收藏  举报