python的闭包和装饰器

闭包

闭包的作用:闭包可以保存外部函数内的变量,不会随着外部函数调用完而销毁。
注意点:由于闭包引用了外部函数的变量,则外部函数的变量没有及时释放,消耗内存。

闭包的形成条件
1. 函数嵌套
2. 内部函数使用了外部函数的变量或者参数
3. 外部函数返回内部函数, 这个使用了外部函数变量的内部函数称为闭包
# 1. 函数嵌套
def func_out():
    # 外部函数
    num1 = 10
    def func_inner(num2):
        # 内部函数
        # 2. 内部函数必须使用了外部函数的变量
        result = num1 + num2
        print("结果:", result)

    # 3. 外部函数要返回内部函数,这个使用了外部函数变量的内部函数称为闭包
    return func_inner

# 获取闭包对象
# 这个new_func就是闭包
# 这里的new_func = func_inner
new_func = func_out()
# 执行闭包
new_func(1)
new_func(10)

案例:闭包的使用

# 外部函数接收姓名参数
def config_name(name):
    # 内部函数保存外部函数的参数,并且完成数据显示的组成
    def inner(msg):
        print(name + ":" + msg)

    print(id(inner))
    # 外部函数要返回内部函数
    return inner

# 创建tom闭包实例(对象)
tom = config_name("tom")
# 创建jerry闭包实例
jerry = config_name("jerry")
# 如果执行tom闭包,因为已经保存了name参数,那么以后在输入的时候都是,tom说:xxx
tom("哥们,过来一下,我们一起玩耍!")
jerry("打死都不去!")
tom("我不吃你")
jerry("谁相信你")

 修改闭包内使用的外部变量

# 1. 函数嵌套
def func_out():
    # 外部函数的变量
    num1 = 10
    def func_inner():
        # 在闭包内修改外部函数的变量
        # num1 = 20  # 本意是修改外部函数变量, 其实是在闭包内定义了一个局部变量
        # 在闭包内修改外部函数的变量需要使用nonlocal关键字
        nonlocal num1
        num1 = 20

        # 2.内部要使用外部函数的变量
        result = num1 + 10
        print(result)

    print("修改前的外部变量:", num1)
    func_inner()
    print("修改后的外部变量:", num1)

    # 3. 返回内部函数
    return func_inner

# 创建闭包对象
new_func = func_out()
new_func()

装饰器

装饰器定义: 对已有函数进行额外的功能扩展, 装饰器本质上一个闭包函数,也就是说他也是一个函数嵌套

装饰器的特点:
1. 不修改已有函数的源代码
2. 不修改已有函数的调用方式
3. 给以后函数添加额外的功能
# 定义装饰器
def decorator(func):  # 如果闭包函数的参数有且只有一个并且是函数类型,那么这个闭包函数称为装饰器
    print("装饰器执行了")
    def inner():
        # 在内部函数里面对已有函数进行装饰
        print("已添加登陆验证")
        func()
    return inner

# 装饰器的语法糖写法: @装饰器名称,装饰器的语法糖就是在装饰以后函数的时候写法更加简单
@decorator  # comment = decorator(comment) 装饰器语法糖对该代码进行了封装  comment=inner
def comment():
    print("发表评论")

# 已添加登陆验证
# 发表评论

# # 调用装饰器对已有函数进行装饰  , comment=inner
# comment = decorator(comment)

# 调用方式不变
comment()

# 装饰器的执行时机: 当当前模块加载完成以后,装饰器会立即执行,对已有函数进行装饰
装饰器的执行时机: 当当前模块加载完成以后,装饰器会立即执行,对已有函数进行装饰
import mydecorator     # 导入有装饰器模块

mydecorator.comment()

装饰器的使用场景

函数执行时间的统计

输出日志信息

# 定义装饰器
def decorator(func):
    def inner():
        # 内部函数对已有函数进行装饰
        # 获取时间距离1970-1-1:0:0:1的时间差
        begin = time.time()
        func()
        end = time.time()
        result = end - begin
        print("函数执行完成耗时:", result)
    return inner

@decorator  # work = decorator(work), work = inner
def work():
    for i in range(10000):
        print(i)
work()
通用的装饰器: 可以装饰任意类型的函数

--------装饰带有参数的函数

# 定义装饰器
def decorator(func):
    # 使用装饰器装饰已有函数的时候,内部函数的类型和要装饰的已有函数的类型保持一致
    def inner(a, b):
        # 在内部函数对已有函数进行装饰
        print("正在努力执行加法计算")
        func(a, b)

    return inner


# 用装饰器语法糖方式装饰带有参数的函数
@decorator  #  add_num= decorator(add_num), add_num=inner
def add_num(num1, num2):
    result = num1 + num2
    print("结果为:", result)

add_num(1, 2)
------装饰带有参数和返回值的函数
def decorator(func):
    # 使用装饰器装饰已有函数的时候,内部函数的类型和要装饰的已有函数的类型保持一致
    def inner(a, b):
        # 在内部函数对已有函数进行装饰
        print("正在努力执行加法计算")
        num = func(a, b)
        return num

    return inner


# 用装饰器语法糖方式装饰带有参数的函数
@decorator  #  add_num= decorator(add_num), add_num=inner
def add_num(num1, num2):
    result = num1 + num2
    return result

result = add_num(1, 2)
print("结果为:", result)
----装饰带有不定长参数和返回值的函数
# 该装饰器还可以成为是通用的装饰器
def decorator(func):
    # 使用装饰器装饰已有函数的时候,内部函数的类型和要装饰的已有函数的类型保持一致
    def inner(*args, **kwargs):
        # 在内部函数对已有函数进行装饰
        print("正在努力执行加法计算")

        # *args: 把元组里面的每一个元素,按照位置参数的方式进行传参
        # **kwargs: 把字典里面的每一个键值对,按照关键字的方式进行传参
        # 这里对元组和字典进行拆包,仅限于结合不定长参数的函数使用
        num = func(*args, **kwargs)
        return num

    return inner


@decorator  # show= decorator(show) show = inner
def show():
    return "哈哈"

result = show()
print(result)
---- 用装饰器语法糖方式装饰带有参数的函数
def decorator(func):
    # 使用装饰器装饰已有函数的时候,内部函数的类型和要装饰的已有函数的类型保持一致
    def inner(*args, **kwargs):
        # 在内部函数对已有函数进行装饰
        print("正在努力执行加法计算")

        # *args: 把元组里面的每一个元素,按照位置参数的方式进行传参
        # **kwargs: 把字典里面的每一个键值对,按照关键字的方式进行传参
        # 这里对元组和字典进行拆包,仅限于结合不定长参数的函数使用
        num = func(*args, **kwargs)
        return num


@decorator  #  add_num= decorator(add_num), add_num=inner
def add_num(*args, **kwargs):
    result = 0

    # args: 元组类型
    # kwargs: 字典类型

    for value in args:
        result += value

    for value in kwargs.values():
        result += value

    return result

result = add_num(1, 2)
print("结果为:", result)

# 错误写法
# if __name__ == '__main__':
#     my = {"a":1}
#     print(**my)
多个装饰器的使用

多个装饰器的装饰过程: 由内到外的一个装饰过程,先执行内部的装饰器,在执行外部的装饰器
def make_div(func):
    print("make_div装饰器执行了")
    def inner():
        # 在内部函数对已有函数进行装饰
        result = "<div>" + func() + "</div>"
        return result
    return inner

# 定义装饰器
def make_p(func):
    print("make_p装饰器执行了")
    def inner():
        # 在内部函数对已有函数进行装饰
        result = "<p>" + func() + "</p>"
        return result
    return inner

# 多个装饰器的装饰过程: 由内到外的一个装饰过程,先执行内部的装饰器,在执行外部的装饰器
# 原理剖析: content = make_div(make_p(content))
# 分步拆解: content = make_p(content),内部装饰器装完成content=make_p.inner
#  content = make_div(make_p.inner)
@make_div
@make_p
def content():
    return "人生苦短,我用python!"

# <p>人生苦短,我用python!</p>
result = content()
print(result)

带有参数的装饰器

带有参数的装饰器,其实就是定义了一个函数,让函数接收参数,在函数内部返回的是一个装饰器

def return_decorator(flag):
    # 装饰器, 装饰器只能接收一个参数并且是函数类型
    def decorator(func):
        def inner(a, b):
            if flag == "+":
                print("正在努力执行加法计算")
            elif flag == "-":
                print("正在努力执行减法计算")
            func(a, b)
        return inner
    # 当调用函数的时候可以返回一个装饰器decorator
    return decorator


# 加法计算
@return_decorator("+")  # decorator = return_decorator("+"), @decorator => add_num=decorator(add_num)
def add_num(a, b):
    result = a + b
    print(result)

add_num(1, 2)

# # 减法计算
@return_decorator("-")
def sub_num(a, b):
    result = a - b
    print(result)

add_num(1, 2)
sub_num(1, 4)

# 带有参数的装饰器,其实就是定义了一个函数,让函数接收参数,在函数内部返回的是一个装饰器
类装饰器: 使用类装饰已有函数
# # 类装饰器: 使用类装饰已有函数
class MyDecorator(object):
    def __init__(self, func):
        self.__func = func

    # 实现__call__这个方法,让对象变成可调用的对象,可调用的对象能够像函数使用
    def __call__(self, *args, **kwargs):
        # 对已有函数进行封装
        print("课已讲完")
        self.__func()

@MyDecorator  # @MyDecorator => show = MyDecorator(show)
def show():
    print("快要下学啦")

# 执行show  # 执行MyDecorator类创建实例对象 -> show() => 对象()
show()

# class AAA(object):
#     pass
#
# a = AAA()
# a()

# 扩展: 函数之所有能够调用是因为函数内部使用__call__
def mytest():
    print("哈哈")

print(dir(mytest))

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值