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
ropon
V2EX  ›  Python

求助,分开写输出是[12, 13, 14, 15];用 for 输出是[20, 21, 22, 23],请问为啥呢

  •  
  •   ropon ·
    ropon · 2018-10-09 15:58:29 +08:00 · 3691 次点击
    这是一个创建于 2272 天前的主题,其中的信息可能已经有所发展或是发生改变。
    def add(a, b):
    return a + b

    def test():
    for r_i in range(4):
    yield r_i

    g = test()
    #0 1 2 3

    # g = (add(2, i) for i in g)
    # g = (add(10, i) for i in g)
    # print(list(g))
    #[12, 13, 14, 15]

    for n in [2, 10]:
    # 0 1 2 3
    # 2 1 2 3
    g = (add(n, i) for i in g)
    #第一次循环 n=2,i 第一次循环 0 相加结果 2 3 4 5
    #第二次循环 n=10,i 第一次循环 2 相加结果 12 13 14 15
    print(list(g))
    #为啥输出[20, 21, 22, 23]
    20 条回复    2018-10-09 23:13:48 +08:00
    xyxc0673
        1
    xyxc0673  
       2018-10-09 16:21:37 +08:00
    打印 add 的参数 a 和 b,可以发现 a 在循环的方式中始终是 10,也就是 n 引用的始终是最后一次循环 n 的值,这和直接传值的方式是不一样的。
    xyxc0673
        2
    xyxc0673  
       2018-10-09 16:33:04 +08:00   ❤️ 1
    @xyxc0673 #1 如果用列表生成式就不会出现这种情况,因为生成器只有在被遍历时才会进行运算。
    xyxc0673
        3
    xyxc0673  
       2018-10-09 16:33:57 +08:00
    @xyxc0673 #2 列表生成式则会一次性进行所有的运算
    ropon
        4
    ropon  
    OP
       2018-10-09 16:43:10 +08:00
    换成列表生产式 g = [add(n, i) for i in g],for 和单独传值结果一致[12, 13, 14, 15],不明白是怎么推算出[20, 21, 22, 23],for 第二次循环会覆盖上次循环的值吧。
    ballshapesdsd
        5
    ballshapesdsd  
       2018-10-09 16:44:40 +08:00
    感觉是 python 语言的糟粕。。
    ropon
        6
    ropon  
    OP
       2018-10-09 17:01:08 +08:00
    如果遍历一次生成器表达式,那么 g 就没值了,生成器表达式 g = (add(n, i) for i in g) for i in g 不会遍历 g 么
    xyxc0673
        7
    xyxc0673  
       2018-10-09 17:07:31 +08:00
    @ropon #6 g 在第一次遍历时没值了,但是你又赋值给 g 了。。。
    ropon
        8
    ropon  
    OP
       2018-10-09 17:18:21 +08:00
    @xyxc0673 逐级遍历吧
    比如一开始 g 是 0 1 2 3,第一次循环 的第一次遍历
    2 1 2 3
    2 3 2 3
    2 3 4 3
    2 3 4 5

    这样理解对么
    goofool
        9
    goofool  
       2018-10-09 17:21:36 +08:00
    n 一直是 10 哇,结果应该是 10+10+i
    ropon
        10
    ropon  
    OP
       2018-10-09 17:25:13 +08:00
    @goofool 10+10+i 是怎么来的呢
    xyxc0673
        11
    xyxc0673  
       2018-10-09 17:25:33 +08:00
    @ropon #8 不懂你在讲哪个方面了。。。
    在循环的方式下,第一次循环时,g 的值为
    (
    add(2, 0),
    add(2, 1),
    add(2, 2),
    add(2, 3)
    )
    第二次循环时,g 的值为
    (
    add(10, add(10, 0)),
    add(10, add(10, 1)),
    add(10, add(10, 2)),
    add(10, add(10, 3))
    )
    ropon
        12
    ropon  
    OP
       2018-10-09 17:30:25 +08:00
    @xyxc0673 原来如此,明白了,感谢
    goofool
        13
    goofool  
       2018-10-09 17:34:08 +08:00
    @ropon 我记得叫 late binding 机制吧,两次计算的时候 n=10
    goofool
        14
    goofool  
       2018-10-09 17:53:14 +08:00 via Android
    @goofool 你把 2 改成 None 或者字符串,你就发现问题了
    savebox
        15
    savebox  
       2018-10-09 17:56:55 +08:00
    g = (add(n, i) for i in g) 改成 g = [add(n, i) for i in g] 就达到要求了
    ropon
        16
    ropon  
    OP
       2018-10-09 20:21:39 +08:00
    @xyxc0673 整理如下,看理解对吗?

    #总结:推导式有,列表推导式、字典推导式、集合推导式,没有元组推导式
    #生成器表达式:(结果 for 变量 in 可迭代对象 if 条件) 生成器表达式可直接获取其对象,对象可直接使用 for 循环,生成器具有惰性机制

    def add(a, b):
    return a + b

    def test():
    for r_i in range(4):
    yield r_i

    g = test()
    #0 1 2 3

    # g = (add(2, i) for i in g)

    #g 的值是
    # (
    # add(2, 0),
    # add(2, 1),
    # add(2, 2),
    # add(2, 3)
    # )

    # g = (add(10, i) for i in g)

    #g 的值是
    # (
    # add(10, add(2, 0)),
    # add(10, add(2, 1)),
    # add(10, add(2, 2)),
    # add(10, add(2, 3))
    # )
    #遍历生成器开始运算输出结果
    # print(list(g))
    #输出[12, 13, 14, 15]

    for n in [2, 10]:
    # g = [add(n, i) for i in g] #列表生成式会一次性进行所有的运算
    # 第一次循环 n=2,i 第一次循环 0 相加结果 2 3 4 5
    # 第二次循环 n=10,i 第一次循环 2 相加结果 12 13 14 15

    g = (add(n, i) for i in g) # 生成器表达式只有在被遍历时才会进行运算
    #第一次循环 n=2,i 从 0 1 2 3 循环,因生成器具有惰性机制,n 并没有取对应值,只是指向对应内存地址,g 的值是
    # (
    # add(n, 0),
    # add(n, 1),
    # add(n, 2),
    # add(n, 3)
    # )
    #第二次循环 n=10,i 从 add(2, 0) add(2, 1) add(2, 2) add(2, 3)循环,同理,g 的值是
    # (
    # add(n, add(n, 0)),
    # add(n, add(n, 1)),
    # add(n, add(n, 2)),
    # add(n, add(n, 3))
    # )
    #for 循环完,系统会释放 n,释放之前 n 先取值,g 的值是
    # (
    # add(10, add(10, 0)),
    # add(10, add(10, 1)),
    # add(10, add(10, 2)),
    # add(10, add(10, 3))
    # )
    #遍历生成器开始运算输出结果
    print(list(g))
    #输出[20, 21, 22, 23]
    ropon
        17
    ropon  
    OP
       2018-10-09 20:57:36 +08:00
    a = [1, 2]
    a[1] = a
    print(a[1])
    补门一个问题
    为啥结果是:
    [1, [...]]
    ruoxin123
        18
    ruoxin123  
       2018-10-09 22:03:07 +08:00
    @ropon
    因为 a[1]是 a 自己啊
    所以会无限循环下去
    你会发现 a[1], a[1][1], a[1][1][1]... 都是[1, [...]]
    xyxc0673
        19
    xyxc0673  
       2018-10-09 22:32:36 +08:00
    @ropon #16

    @goofool #13 说的准确一些,因为 lambda 表达式放在列表生成式也会出现这种现象,具体的讲解可以搜索关键词 late binding
    ropon
        20
    ropon  
    OP
       2018-10-09 23:13:48 +08:00
    @ruoxin123
    a[1]=a
    第一次 a=[1, [1, 2] ] 第二次 a=[1, [1, [1, 2] ] ] 第三次 a=[1, [1, [1, [1, 2] ] ] ]
    这样理解对吗
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2437 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 15:51 · PVG 23:51 · LAX 07:51 · JFK 10:51
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.