关于装饰器、生成器的一些心得

       网上关于装饰器的文章很多,这里就不多啰嗦了,我总结起来就是两句话:装饰器是一个可调用对象,调用装饰器作用于它要装饰器的对象。装饰器是一个可调用对象,一切可调用对象都可以作为装饰器,函数、方法、类、类的实例等等都可以作为装饰器。

      @wrapper                                                                                                                                              

      def f(*args, **kwargs):pass                                                                                                                  

等价于 f = wrapper(f)

      @wrapper(0)                                                                                                                                                

      def f(*args, **kwargs):pass                                                                                                                    

等价于 f = wrapper(0)(f)   

        使用装饰器,记住上面的两句话,就足矣,记住,解释器用装饰器装饰一个对象就做了两件事情,首先调用装饰器,作用于它要装饰器的对象,然后把它装饰的对象所绑定的名字绑定到装饰器返回的对象上,就这点事情,没有其他。

       让我们看一个容易让我们犯错的例子,也许它出乎了你的意料,但却是在情理之中

from functools import wraps
def wrapper(f):
    @wraps(f)
    def inner(*args, **kwargs):
        print("call f")
        return f(*args, **kwargs)
    return inner

@wrapper
def fib(n):
    if 0 == n:
        return 0
    if 1 == n:
        return 1
    return fib(n - 2) + fib(n - 1)
fib(3)

        执行的结果是”call f”被多次打印了,原本我的预期是只打印一次,为什么会打印多次呢,按照fib的定义,它递归调用它自己,可是被装饰之后,它递归调用的不再是它自己了,而是inner,因为装饰之后fib被绑定到inner上了。查看一下字节码就能看出端倪。

        从字节码里看出来,调用一个函数的过程是先LOAD_GLOBAL把函数对象压到栈顶(它通过名字fib到全局的namespace中去取得函数对象),压到栈顶,然后把参数入栈,最后调用函数CALL_FUNCTION。       

       关于生成器,在我上一篇文章里提到,生成器其实就是一个可以多次调用返回的函数,是函数的加强版,之所以如此,是因为python对调用栈的实现不同于c语言/汇编,汇编里面的调用栈是一段连续的内存,使用栈也只借助于几个寄存器,主要是bp和sp,一个指向栈帧的底部,一个指向栈顶,函数调用返回也就是在修改这两个寄存器,根本不可能做到说把一个函数的栈帧出栈然后再入栈,因此在c语言/汇编里面函数是不可能被多次调用返回的,而python不一样,python是把调用栈实现为一个list,函数的frame是从堆上申请的内存,因此它可以被压入栈中,弹出来,然后再压入栈中,这也就有决定了在python中有了生成器这样可以多次调用返回的对象了。frame对象上有一个属性f_back,它指向的是它的调用者的frame。

        对生成器的使用,这里提一下抛入一个异常进去的场景,算是我的一个笔记。

        1)生成器未启动,throw一个异常进去,会导致生成器终止,异常被抛回来。

        2)throw一个异常进去,生成器没有捕获异常,生成器终止,异常被抛回来。

        3)throw一个异常进去,生成器捕获了异常,生成器继续。

        4)close生成器,生成器捕获了异常,生成器继续执行,直到遇到yield或者return,遇到yield,生成器终止,抛回来一个RunTimeError异常,遇到return,生成器正常终止。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

__空无一人__

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值