V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
Alerta
V2EX  ›  Python

Python 关于后期绑定的问题,我开始怀疑人生了,求大神解析

  •  
  •   Alerta · 2018-08-23 21:02:03 +08:00 · 2744 次点击
    这是一个创建于 2320 天前的主题,其中的信息可能已经有所发展或是发生改变。

    当我直接 return 一个匿名函数的时候返回 0,2,4,6

    def testFun():
        return(lambda x : i*x for i in range(4))
    for everyLambda in testFun():
        print(everyLambda(2))
    
    & python test.py
    0
    2
    4
    6
    

    但是当我把匿名函数作为一个 temp 来返回的时候,结果却是 6,6,6,6

    def testFun():
        temp = [lambda x : i*x for i in range(4)]
        return temp
    for everyLambda in testFun():
        print(everyLambda(2))
    
    & python test.py
    6
    6
    6
    6
    

    求大神解释解释,这是为什么呀,急求明天就要面试了好紧张

    14 条回复    2018-08-27 16:34:47 +08:00
    XiaoxiaoPu
        1
    XiaoxiaoPu  
       2018-08-23 21:13:55 +08:00
    两段代码的区别不是“把匿名函数作为一个 temp 来返回”,而是第一段代码返回的是生成器,在 for 循环的时候 i 才实际增加,第二段代码返回的是数组,在 for 循环开始的时候 i 已经增加到 3 了
    newmind
        2
    newmind  
       2018-08-23 21:27:42 +08:00
    第一个是括号(), 为生成器, 返回 generator
    第二个是中括号[], 为列表生成式, 返回数组
    HelloAmadeus
        3
    HelloAmadeus  
       2018-08-23 21:34:24 +08:00
    ```
    tmp = [lambda x: x*i for i for range(4)]
    ```
    返回的是:
    ```
    tmp = [lambda x: x*3, ...]
    ```
    ```
    tmp = (lambda x: x*i for i for range(4))
    返回的是:
    ```
    tmp = (lambda x: x*0, lambda x: x**1, ...) # 这是一个生成器
    ```

    列表表达式是即时计算的, 而生成器是迭代时才会计算. 返回的 lamda 匿名函数在查找 i 变量的时候, 列表表达式已经算计完了, 此时的 i 值为 3, 所以计算的返回结果是 6, 而在生成器计算的时候, `for i in range(4)` 的计算是惰性的, 只有你去迭代生成器的时候, i 的值才会 +1. 所以 lambda 表达式查找的 i 变量是 0, 1, 2, 3 的序列.
    lixm
        4
    lixm  
       2018-08-23 22:23:46 +08:00   ❤️ 1
    ```python
    from dis import dis

    f1 = next((lambda x : i*x for i in range(4)))
    dis(f1)
    f2 = [lambda x : i*x for i in range(4)][0]
    dis(f2)
    ```
    都取第 0 个函数出来, 反编译一下就知道, 这两个唯一的区别就是 i,

    f1 是 LOAD_DEREF i 作为闭包传入
    f2 是 LOAD_GLOBAL i 作为全局变量传入

    ```python
    print(f1.func_closure[0].cell_contents)
    ```
    可以看出 作为闭包传入的 i 的值为 0

    ```python
    print(f2.func_globals['i'])
    ```
    可以看出 作为全局变量传入的 i 的值为 3

    为什么会这样呢?因为 Python 里,一切都是引用, 在第一个例子里, 因为生成器是惰性求值, 你可以理解在执行 next, 或者 for 循环的时候, 才求出 i 的值, 所以 i 的值从 0 递增到 3。 在第二个例子里, 列表并不是惰性求值,i 作为一个引用, 值已经变为 3 了

    可能说的不是很清楚, 抱歉
    lolizeppelin
        5
    lolizeppelin  
       2018-08-23 22:27:23 +08:00 via iPad
    这不是 python 的问题。 我所知的语言里只有 erlang 不出这个问题
    js 之类的一鸟样
    cyrbuzz
        6
    cyrbuzz  
       2018-08-24 08:54:02 +08:00
    我比较好奇有什么情况下会用这样的写法。
    ipwx
        7
    ipwx  
       2018-08-24 10:03:43 +08:00
    你需要这么写

    https://ideone.com/mrZkgF
    whoami9894
        8
    whoami9894  
       2018-08-24 23:12:17 +08:00 via Android
    补充一下,python 中只有 def,class,lambda 会产生作用域,所以这里 for 循环结束后 i 依然存在为 4,这里方括号列表解析生成的 lambda 可以看作是宏,所以会得到 6666 的结果
    josephshen
        9
    josephshen  
       2018-08-26 17:55:53 +08:00 via iPhone
    我告诉你上面的解释是出现了显现给的解释,原因根源是 python 这门语言的设计问题,导致了分不清申明和赋值
    josephshen
        10
    josephshen  
       2018-08-26 17:57:09 +08:00 via iPhone
    显示->现象
    Alerta
        11
    Alerta  
    OP
       2018-08-26 23:02:45 +08:00
    @HelloAmadeus 感谢感谢 一下子明白了
    Alerta
        12
    Alerta  
    OP
       2018-08-26 23:05:03 +08:00
    @lixm 感激,虽然一开始不是很懂 但是很欣赏你的思考方式,从最根源的现象去推导原因,学习了
    Alerta
        13
    Alerta  
    OP
       2018-08-26 23:07:41 +08:00
    @cyrbuzz 一个是为了应对面试,二来只是自己无聊刷刷奇怪的题目而已。。。
    U87
        14
    U87  
       2018-08-27 16:34:47 +08:00
    @lixm 真的 6, 反向思考...赞一下
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3381 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 74ms · UTC 04:59 · PVG 12:59 · LAX 20:59 · JFK 23:59
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.