zoukankan      html  css  js  c++  java
  • [动态语言]python的闭包问题

    首先声明,我用的是2.7.1版本的CPython。

    第一个问题,闭包中的upvalue不可修改:

     1 def foo():
    2 i = 0
    3 def _foo():
    4 i += 1
    5 print i
    6 return _foo
    7
    8 f = foo()
    9 f()
    10 f()
    11 f()

    错误:local variable 'i' referenced before assignment

    可以理解,不用global关键字的话,修改全局变量也会遇到问题。因此这个问题其实是不能修改所有外层变量。python3引入了nonlocal来处理这个问题。如果一定要在python3之前的版本中修改upvalue,可以将upvalue放在list中再修改list项。

    python和lua/js的选择相反,不特殊声明的情况下,后两者是访问外层变量,除非显示的用local/var,才是访问局部变量;而python是默认访问局部变量,除非显示用global/nonlocal。python3之前的global关键字太不够用了。

    python这样默认是局部变量,可以让你写出简洁的普通函数,而lua那样默认是外层变量,则是鼓励编写匿名函数,因为匿名函数一般都会用作闭包。

    第二个问题,for/try等作用于中的变量居然是全局的!

    1 for i in range(5):
    2 pass
    3 print i
    4
    5 try:
    6 j = 5
    7 finally:
    8 pass
    9 print j

    输出:4,5

    试想在for之前使用过i这个全局变量,for过后,i被修改了!

    一个旨在尽量隐藏语法细节的优秀语言,居然会有这种行为?20年都没有修正,那一定是有它这样做的原因吧...

    再来看看for当中的i是全局,这种不符直觉的行为的影响:

    1 a = [(lambda n: n * i) for i in range(3)]
    2
    3 for f in a:
    4 print f(2)

    期望的结果是:0,2,4

    实际的结果是:4,4,4

    因为i是全局的,所以生成的所有lambda表达式其实代码相同了!

    用一个hack来解决:

    a = [(lambda n, i=i: n * i) for i in range(3)]

    for f in a:
    print f(2)

    这里为lambda添加了一个默认参数i,因此n * i中的i不再是upvalue,而是参数!又因为,python中的默认参数值是保存在函数对象内的(__defaults__),所以,生成每个匿名函数时,都会读取i的即时值,并保存起来。

  • 相关阅读:
    Linux命令之vi
    CentOS7 查看IP
    Linux下mysql的命令
    @RequestMapping注解的参数说明
    springboot处理不同域的前端请求
    vue-cli4 取消关闭eslint 校验代码
    springmvc请求乱码
    访问静态资源报404错误
    eclipse创建Maven项目时pom.xml报错
    spring中的xml配置文件里报错
  • 原文地址:https://www.cnblogs.com/cbscan/p/2320957.html
Copyright © 2011-2022 走看看