Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/python-3.x/19.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 3.x Python3生成器令人费解的行为_Python 3.x - Fatal编程技术网

Python 3.x Python3生成器令人费解的行为

Python 3.x Python3生成器令人费解的行为,python-3.x,Python 3.x,以下两个Python3代码段的不同之处在于我认为没有什么区别。然而,当在MacOSX下的Python3.5中从空闲运行时,它们会产生不同的结果。在运行每个代码片段之前,我重新启动了pythonshell 片段1: ## The following prints [5, 7] as expected a = (k for k in range(2, 10)) # generator for [2, 3, 4, 5, 6, 7, 8, 9] p = next(a)

以下两个Python3代码段的不同之处在于我认为没有什么区别。然而,当在MacOSX下的Python3.5中从空闲运行时,它们会产生不同的结果。在运行每个代码片段之前,我重新启动了pythonshell

片段1:

## The following prints [5, 7] as expected
a = (k for k in range(2, 10))    # generator for [2, 3, 4, 5, 6, 7, 8, 9]
p = next(a)                      # p is 2
a = (k for k in a if k % p != 0) # remove multiples of 2: generator for [3, 5, 7, 9]
q = next(a)                      # q is 3
a = (k for k in a if k % q != 0) # remove multiples of 3: generator for [5, 7]
print(list(a))                   # prints [5, 7]
代码片段2:[唯一的区别是在第四行和第五行代码中使用p而不是q。]

## The following prints [4, 5, 7, 8]
a = (k for k in range(2, 10))
p = next(a)
a = (k for k in a if k % p != 0)
p = next(a)                      # Using p instead of q
a = (k for k in a if k % p != 0) # Ditto
print(list(a))                   # prints [4, 5, 7, 8]
因此,重用对象名p似乎有着深远的影响


我错过了什么?我试图将示例显著缩小,但没有成功。

关键在于以下两行:

a = (k for k in a if k % p != 0)
p = next(a)                      # Using p instead of q
“新
a
”从“旧
a
”中生成与条件
k%p!=0
。但是,该条件仅在从生成器
a
请求新元素时进行评估,并且它使用
p
的值。在您的情况下,这将是3(而不是人们所期望的2)


详细解释:

让我们跟踪代码中发生了什么。我使用了三个不同的
a
变量(
a
a2
a3
)来避免混淆

a = (k for k in range(2, 10))
将创建一个生成器
A
,此时
list(A)
将生成
[2,3,4,5,6,7,8,9]

p = next(a)
a2 = (k for k in a if k % p != 0)
第一个值从
a
中消耗,并分配给变量
p
<代码>p变为
2
,此时
列表(a)
将导致[3,4,5,6,7,8,9]

p = next(a)
a2 = (k for k in a if k % p != 0)
将创建一个新的生成器。它生成由
a
生成的与条件
k%p!=0
。由于
p
当前等于
2
列表(a2)
将导致
[3,5,7,9]
(顺便说一句,生成器
a
也将在该过程中耗尽)

从生成器
a2
请求一个新值。后者从
a
请求一个值,获取
3
,并检查它是否符合上述条件
k%p!=0
。由于
p
当前等于
2
,它确实是这样,并且
a2
产生
3
,这成为
p
的新值

由于从
a
中消耗了一个值,
列表(a)
此时将导致
[4,5,6,7,8,9]
。但是,
列表(a2)
现在将导致
[4,5,7,8]
!那是因为
k%p!=0
将使用
p
的当前值,即
3
,使
a2
跳过
6
9
的值

a3 = (k for k in a2 if k % p != 0)
新的生成器
a3
a2
基本相同,因为它们具有相同的条件

print(list(a3))
发电机
a3
耗尽,而发电机
a2
又会耗尽
a
中的值,从而产生观察结果
[4,5,7,8]


在这里,
a
产生了
[4,5,6,7,8,9]
,但是
3的倍数已经被
a2
过滤掉了,而
a3
没有过滤任何其他内容(因为它基本上与
a2
相同)。

这是前面问题的重复(我承认,不是很容易找到)--生成器表达式在使用时拾取
p
的值,而不是在定义时。这里应该有“后期绑定生成器表达式”之类的东西。一个较短的例子是
p=2;a=(k表示范围(2,10)内的k,如果k%p==0);p=3;打印(列表(a))
它打印
3、6、9
p
的值在生成器执行之前(转换为列表时)不会使用。啊,我明白了。谢谢,谢谢,你的解释很清楚。是否有一种简单的方法可以将生成器中的值“冻结”为定义生成器时的值,而不是使用生成器时的值?或者这是个坏主意?您需要确保的唯一一件事是生成器中使用的变量不会被修改(
p
)。您发布的第一个代码片段已经完全避免了这个问题,因为它使用了不同的变量,即
q
。另一个选项是创建一个函数,使用它自己的本地
p
变量为您创建一个生成器:
def make_-gen(gen,p):返回(k表示gen中的k,如果k%p!=0)
,然后
p=2;a2=制造发电机(a,p)
p
的后续修改不会影响
a2
,因为后者将使用本地
p
变量。