Python日常笔记(36) - 装饰器

闭包

在函数内部再定义一个函数,并且这个函数用到了外边函数的变量,那么将这个函数以及用到的一些变量称之为闭包

案例:


# 闭包
def print_info(name, age):
    def info(sex):
        print(f"姓名:{name},年龄:{age},性别:{sex}")

    # 只是将一个内部方法引用返回,记住不能加括号,如果加括号就变成了执行函数了
    return info


# 接收的是一个函数引用
show_info = print_info("zhangsan", 18)
show_info("女")

打印结果:

注意:函数,匿名函数(Lambda),闭包,对象,当做实参的区别是:
1.匿名函数能够完成基本简单功能,传递是这个函数的引用,只有功能
2.普通函数能够完成复杂的功能,传递这个函数的引用,只有功能
3.闭包能够完成将较为复杂的功能,并且每个函数都需要返回闭包函数的引用,传递是这个闭包中的函数以及数据,因此传递功能+数据
4.对象能够完成最复杂的功能,传递是很多数据+很多功能,因此传递是功能+数据

修改闭包上级函数的局部变量

修改全局变量使用global,那么要修改闭包上级函数的局部变量使用nonlocal语法:

nonlocal 变量名

代码:

# 修改闭包局部变量
def print_info(name, age):
    number = 100

    def info(sex):
        # 修改上级函数变量使用
        nonlocal number
        print(f"姓名:{name},年龄:{age},性别:{sex},number:{number}")
        number = 10
        print(f"姓名:{name},年龄:{age},性别:{sex},number:{number}")

    # 只是将一个内部方法引流返回
    return info


# 接收的是一个函数引用
show_info = print_info("zhangsan", 18)
show_info("女")

打印结果:

注意:如果在闭包中使用number变量,之后在定义一个number变量时,python解释器会报错,如下代码所示

装饰器

总体来说装饰器主要是来让代码更加简短,也方便维护代码而不需要修改原有已经调用的函数中的代码。也就是将复杂的代码可以变得很简洁。

自定义一个装饰器的演变代码:

# 创建一个闭包
def fun_01(func):
    def a():
        print("我是闭包============")
        # 执行func方法
        func()

    return a


# 创建一个普通函数
def fun_02():
    print("我是func_02")


# 将接收的变量名与上面的函数名相同,这个时候fun_02就有了新的指向为:fun_01
# 而参数fun_02已经将原有的函数引用传递给了fun_01
fun_02 = fun_01(fun_02)
fun_02()

以上的代码看上去有点绕,也不方便理解,现在使用装饰器方式来简化代码

# 创建一个闭包
def fun_01(func):
    def a():
        print("我是闭包============")
        # 执行func方法
        func()

    return a


# 装饰器
@fun_01
def fun_02():
    print("我是func_02")


fun_02()

打印结果如上图一样

多参数的装饰器

# 创建一个闭包
def fun_01(func):
    # 这里的*args, **kwargs表示形参
    def a(*args, **kwargs):
        print("我是闭包============")
        # 执行func方法,这里的*args, **kwargs表示拆包
        func(*args, **kwargs)

    return a


# 装饰器
@fun_01
def fun_02(num, *args, **kwargs):
    print(f"{num},{args},{kwargs}")


fun_02(1, 2, 3, name="张三")

打印结果:

装饰器返回值

代码:

# 创建一个闭包
def fun_01(func):
    # 这里的*args, **kwargs表示形参
    def a(*args, **kwargs):
        print("我是闭包============")
        # 执行func方法,这里的*args, **kwargs表示拆包,
        # 闭包返回的值就是装饰器的返回值
        return func(*args, **kwargs)

    return a


# 装饰器返回值
@fun_01
def fun_02(num, *args, **kwargs):
    print(f"num={num},**args={args},**kwargs={kwargs}")
    return "我是装饰器"


test = fun_02(1, 2, 3, name="张三")
print("调用fun_02的返回值", test)

打印结果:

注意:如果调用fun_02装饰器有返回值的函数,必须在闭包中也需要有返回return func(*args, **kwargs),才有效,否则将返回None。通用装饰器一般可以将闭包中的函数都写上返回值,因为它不会影响调用方。

多装饰器

代码:

# 创建一个闭包
def fun_01(func):
    def a():
        return "<td>" + func() + "</td>"
    return a


# 创建一个闭包
def fun_02(func):
    def a():
        return "<h1>" + func() + "</h1>"
    return a


# 装饰器返回值
@fun_01
@fun_02
def fun_03():
    return "我是装饰器"


print(fun_03())

打印结果:

注意:哪个装饰器在前面就先执行哪个装饰器.

类装饰器

class MyClass(object):

    def __init__(self, func):
        self.func = func

    def __call__(self, *args, **kwargs):
        return self.func


# 类当做装饰器的原始代码 func = MyClass(func)
@MyClass
def func():
    return "我是装饰器"

# func()获取的就是一个方法引用,再加上一个括号也可以执行
print(func()())

带参数的装饰器

代码:

def set_level(number):
    def set_func(func):
        def call_func():
            if number == 1:
                print(f"=========调用的number={number}==========")
            elif number == 2:
                print(f"=========调用的number={number}==========")
            # 返回被装饰的函数
            return func

        return call_func

    return set_func


# 带参数的装饰器,先执行set_level方法,有一个参数,
# 返回的是set_func()函数的引用,最后还是调用的这个@set_func装饰器
# 这样的好处就是固定一些不可修改的值
@set_level(1)
def func1():
    return "我是装饰器func1"


@set_level(2)
def func2():
    return "我是装饰器func2"


# func()获取的就是一个方法引用,再加上一个括号也可以执行
print(func1()())
print(func2()())

注意:带有参数的装饰器,只是为了固定一些不可更改的参数,最终还是返回的原始装饰器的引用@set_func

作者:阿超
原创公众号:『Python日常笔记』,专注于 Python爬虫等技术栈和有益的程序人生,会将一些平时的日常笔记都慢慢整理起来,也期待你的关注和阿超一起学习,公众号回复【1024】优质资源。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值