Python装饰器与闭包

说到python中的装饰器,很容易联想到设计模式中有个模式叫做装饰器模式,设计模式中的那个装饰器模式的本质其实就是装饰器类和被装饰类实现共同的接口,装饰器类中持有被装饰类的对象并且覆写被装饰类中的方法,覆写的方法中一边通过被装饰类的对象调用该对象自己的方法,一边自定义自己的装饰。python中的装饰器和上面的思想也是类似的。

1. python装饰器的一个简单实例实现

def logging(fn):
    def inner():
        print("被装饰函数执行之前log一下~")
        fn()
        print("被装饰函数执行之后log一下~")
    return inner


@logging
def fun():
    print("执行被装饰函数...")


if __name__ == '__main__':
    fun()

在这里插入图片描述

其中 @logging属于一种语法糖,作用上等价于fun = logging(fun)@logging根据装饰函数的函数名不同可以为 @xxx

2. python中装饰器的实现原理

把上面代码还原成不使用语法糖的格式也许更容易理解:

# 装饰函数
def logging(fn):
    def inner():
        print("装饰函数执行之前log一下~")
        fn()
        print("被装饰函数执行之后再log一下~")
    return inner

# 被装饰函数
def fun():
    print("执行被装饰函数...")


if __name__ == '__main__':
    fun = logging(fun)
    fun()

上面说过,@logging只是一种语法糖,目的是避免开发者自己手动的fun = logging(fun),繁琐而且容易出错,所以上面两段代码本质上是一样的,毫无区别。

从最后两行代码可以看出:此fun(main中的fun)非彼fun(main外的fun),二者只是同名,或者说,main外的fun已经被main中的fun覆盖了。此时的对应关系是:
1.main中的 fun <=> inner
2.main外的 fun <=> fn

最后的 fun() 其实调用的是 inner()。这,就是python中装饰器的实现原理。

3. 装饰带有不定长参数和返回值的函数实例

def logging(fn):
    def inner(*args, **kwargs):
        print("检查参数类型是否有误~")
        return fn(*args, **kwargs)	# 注意此处+return 没有的话会导致res为None
    return inner


@logging
def fun(*args, **kwargs):
    result = 0
    for i in args:
        result += i
    for i in kwargs.values():
        result += i
    return result


if __name__ == '__main__':
    res = fun(1, 2, 3, a=4, b=5)
    print(res)

这里要提别提一下如果fun为带返回值的函数,第4行fn前面的return必不可少,否则会造成print(res)的时候结果为None。原因还是因为main在执行fun(1, 2, 3, a=4, b=5)时此时的fun已经是inner了,return fn(*args, **kwargs)在本质上等价于return return fun(*args, **kwargs)(这里的fun指的是被装饰之前的fun)。
在这里插入图片描述

4. 拓展:闭包

闭包个人理解和装饰器其实就是一回事,区别在于闭包传进去的参数可以不是函数,对于接收inner对象的函数名也没有特殊要求。而装饰器要求传进去的参数必须为函数类型(就是被装饰的函数),接收inner的函数名也必须和传入的函数名相同。

下面贴一下关于闭包的确切定义:
1.在函数嵌套(函数里面再定义函数)的前提下
2.内部函数使用了外部函数的变量(还包括外部函数的参数)
3.外部函数返回了内部函数

常规思路下对方法调用的理解是:方法在被调用时入栈,执行完时出栈销毁,其中保存的变量也会随之一并销毁,但在闭包形成时,外部函数在调用完成并返回内部函数对象时,检测到内部函数使用了自己的局部变量,便会将自己所被使用到的局部变量与内部对象绑定在一起返回。

闭包的一个简单实例,实现求一个数的n次方:

# 定义一个闭包
def outer(n):
    def inner(num):
        return num ** n
    return inner


if __name__ == '__main__': 
    fun1 = outer(3)         # 求3次方
    res1 = fun1(4)          # 求4的3次方
    print(f"res1:{res1}")
    
    fun2 = outer(2)         # 求2次方
    res2 = fun2(8)          # 求8的二次方
    print(f"res2:{res2}")

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值