python中何时使用闭包?

       作为一pythoner,对闭包肯定都不陌生。

       计算机科学中,闭包(Closure),又称词法闭包(Lexical Closure)或函数闭包(function closures),是引用了自由变量的函数。通俗点说闭包是指有权访问另一个函数作用域中变量的函数,创建闭包的最常见的方式是在一个函数内创建另一个函数,被创建的函数可以访问到外部函数的局部变量。那么,为什么要在一个函数内定义另一个函数,让内部函数访问外部函数的局部变量?像有些语言,比如c语言,就没有闭包这种东西,可见闭包也并不是不可或缺的东西,哪怕在python里面,一切用到闭包的地方,其实都可以去掉闭包给出一个无闭包的等价实现。

      比如装饰器,就使用了闭包。

def wrapper(func):
    def inner(*arg, **kwarg):
        ret = func(*arg, **kwarg)
        return ret
    return inner 
@wrapper
def fn():
    print("fn called")
fn()

      下面是等价的去闭包的实现。

class wrapper:
    def __init__(self, func):
        self.func = func
    def __call__(self, *args, **kwargs):
        ret = self.func(*args, **kwargs)
        return ret
@wrapper
def fn():
    print("fn called")
fn()

       由此可见,闭包并非必须,但是没有闭包,只能像上面那样,要用类去实现,麻烦很多。你可能会想,在一个函数内部定义一个函数,在函数执行期间生成一个函数,是不是会拖慢效率,其实不然,看一下wrapper函数的字节码,

  2           0 LOAD_CLOSURE             0 (func)

              2 BUILD_TUPLE              1

              4 LOAD_CONST               1 (<code object inner at 0x7fa992c25d40, file “”, line 2>)

              6 LOAD_CONST               2 ('wrapper.<locals>.inner')

              8 MAKE_FUNCTION            8 (closure)

             10 STORE_FAST               1 (inner)

  5          12 LOAD_FAST                1 (inner)

             14 RETURN_VALUE

Disassembly of <code object inner at 0x7fa992c25d40, file "", line 2>:

  3           0 LOAD_DEREF               0 (func)

              2 LOAD_FAST                0 (arg)

              4 BUILD_MAP                0

              6 LOAD_FAST                1 (kwarg)

              8 DICT_MERGE               1

             10 CALL_FUNCTION_EX         1

             12 STORE_FAST               2 (ret)

  4          14 LOAD_FAST                2 (ret)

             16 RETURN_VALUE

        字节码里只是多了个MAKE_FUNCTION的执行,inner函数的code object是在源文件编译(生成pyc文件)阶段生成的,开销比较大的词法分析,语法分析和生成代码的动作都不是在执行wrapper函数时进行的,MAKE_FUNCTION无非就是把code object,函数名,再加上自由变量(外部函数的局部变量)等一起打包成一个函数对象。

         所以闭包是一个很方便的语言特性,动态语言支持闭包的很多,javascript、PHP等都支持闭包。

         闭包的应用场景有哪些,什么时候用到闭包?

         装饰器是一个典型的应用场景。

        还有一个应用场景,就是函数递归调用且各级调用要共享数据的情况下,如果不用闭包,只能把共享的数据对象作为参数传来传去,代码很难看。

        作者另一篇文章,整数的拆分,给了两个实现,一个用类实现,没有用到闭包,一个用闭包实现,见 正整数的拆分算法

       下面给出一个既不用类,又不用闭包的普通函数的实现。

def intization(res, n, frm):
    for i in range(frm, int(n / 2) + 1):
        res.append(i)
        yield from intization(res, n - i, i)
        res.pop()

    res.append(n)
    yield res
    res.pop()
res = []
for i in intization(res, 5, 1):
    print(i)

        如果把它作为一个公用的接口提供给项目组使用,会很难堪,使用者在调用函数时需要传一个空的list和一个正整数1,会感到很别扭,我也要跟使用者解释,为什么要这么传。好的实现,应该是在函数调用时候只传入被拆分的n,然后得到拆分的结果,上面的实现非常尴尬,会给人很菜鸟的感觉,使用闭包,就避免了这种尴尬。

  • 22
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

__空无一人__

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

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

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

打赏作者

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

抵扣说明:

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

余额充值