地图与列表; 为何不同的行为?

cos*_*uke 13 python functional-programming python-3.x

在为Bayes'Nets程序实现"变量消除"算法的过程中,我遇到了一个意外的错误,这是一系列对象的迭代映射转换的结果.

为简单起见,我将在这里使用类似的代码:

>>> nums = [1, 2, 3]
>>> for x in [4, 5, 6]:
...     # Uses n if x is odd, uses (n + 10) if x is even
...     nums = map(
...         lambda n: n if x % 2 else n + 10, 
...         nums)
...
>>> list(nums)
[31, 32, 33]
Run Code Online (Sandbox Code Playgroud)

这绝对是错误的结果.由于[4,5,6]包含两个偶数,10因此每个元素最多应加两次.我在VE算法中也遇到了意想不到的行为,因此我修改它以在每次迭代后将迭代map器转换为a list.

>>> nums = [1, 2, 3]
>>> for x in [4, 5, 6]:
...     # Uses n if x is odd, uses (n + 10) if x is even
...     nums = map(
...         lambda n: n if x % 2 else n + 10,
...         nums)
...     nums = list(nums)
...
>>> list(nums)
[21, 22, 23]
Run Code Online (Sandbox Code Playgroud)

根据我对可迭代的理解,这种修改不应该改变任何东西,但确实如此.显然,在-ed版本中n + 10,not x % 2案例的转换应用次数较少list.

在发现这个错误后,我的贝叶斯网络程序也运行良好,但我正在寻找解释它为何发生的原因.

awe*_*oon 12

答案很简单:在Python 3中map是一个惰性函数,它返回一个可迭代对象(在Python 2中它返回一个list).让我为你的例子添加一些输出:

In [6]: nums = [1, 2, 3]

In [7]: for x in [4, 5, 6]:
   ...:     nums = map(lambda n: n if x % 2 else n + 10, nums)
   ...:     print(x)
   ...:     print(nums)
   ...:     
4
<map object at 0x7ff5e5da6320>
5
<map object at 0x7ff5e5da63c8>
6
<map object at 0x7ff5e5da6400>

In [8]: print(x)
6

In [9]: list(nums)
Out[9]: [31, 32, 33]
Run Code Online (Sandbox Code Playgroud)

注意In[8]- 值x是6.我们也可以转换lambda函数,传递map给跟踪值的值x:

In [10]: nums = [1, 2, 3]

In [11]: for x in [4, 5, 6]:
   ....:     nums = map(lambda n: print(x) or (n if x % 2 else n + 10), nums)
   ....:     

In [12]: list(nums)
6
6
6
6
6
6
6
6
6
Out[12]: [31, 32, 33]
Run Code Online (Sandbox Code Playgroud)

因为map是懒惰的,它会评估何时list被调用.然而,xis 的值6就是它产生令人困惑的输出的原因.nums在循环内部进行评估会产生预期的输出.

In [13]: nums = [1, 2, 3]

In [14]: for x in [4, 5, 6]:
   ....:     nums = map(lambda n: print(x) or (n if x % 2 else n + 10), nums)
   ....:     nums = list(nums)
   ....:     
4
4
4
5
5
5
6
6
6

In [15]: nums
Out[15]: [21, 22, 23]
Run Code Online (Sandbox Code Playgroud)

  • "它评估x值何时变为6".相反,它评估何时在地图对象上调用`list`,这在`x = 6`时发生. (2认同)

Blc*_*ght 5

问题与x您正在创建的lambda函数如何访问变量有关.Python的作用域的工作方式,lambda函数将x在调用它们时始终使用外部作用域的最新版本,而不是它们定义时的值.

由于map是懒惰的,因此直到循环之后才会调用lambda函数(当你map通过传递它们来使用嵌套的s时list),因此,它们都使用最后一个x值.

要使每个lambda函数保存x定义它们时的值,请x=x像这样添加:

lambda n, x=x: n if x % 2 else n + 10
Run Code Online (Sandbox Code Playgroud)

这指定了一个参数及其默认值.默认值将在定义lambda时进行计算,因此当稍后调用lambda(没有第二个参数)时,x表达式内部将是保存的默认值.