【问题标题】:Trouble with applying python 2 code in python 3在 python 3 中应用 python 2 代码的问题
【发布时间】:2016-01-17 20:13:59
【问题描述】:

为了了解 lambda,我关注了这个 tutorial,并遇到了这个关于计算素数的示例(python 2.x):

nums = range(2,50)
for i in range(2,8):
    nums = filter(lambda x: x == i or x % i, nums)

print (list(nums))

打印

[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47]

但是,在 python 3.4 中尝试此操作时,它产生了意外行为:

nums = range(2,50)
for i in range(2,8):
    nums = filter(lambda x: x == i or x % i , nums)

print(list(nums))

打印

[2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 16, 17, 18, 19, 20, 22, 23, 24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 36, 37, 38, 39, 40, 41, 43, 44, 45, 46, 47, 48]

我不明白为什么会有差异。我知道 filter 在 python 3 中返回一个过滤器对象(而不是一个列表),但据我所知,这不应该影响结果。

删除 for 循环会产生正确的结果:

>>> nums = range(2,50)
>>> nums = filter(lambda x: x == 2 or x % 2, nums)
>>> nums = filter(lambda x: x == 3 or x % 3, nums)
>>> nums = filter(lambda x: x == 4 or x % 4, nums)
>>> nums = filter(lambda x: x == 5 or x % 5, nums)
>>> nums = filter(lambda x: x == 6 or x % 6, nums)
>>> nums = filter(lambda x: x == 7 or x % 7, nums)
>>> print(list(nums))
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47]

我希望有人能启发我,因为我很好奇发生了什么。

【问题讨论】:

  • rangefilter 在 Py3 中返回对象,而在 python2 中返回列表。
  • @BhargavRao:这距离解释这种行为还有很长的路要走。
  • @BrenBarn 简单的改变,filter(lambda x: x == i or x % i , list(nums)) 将在 python 3 中工作 :) 但我相信你可以更好地解释。
  • @BhargavRao:这距离解释行为还有很长的路要走。
  • 是的,我正在懒惰地评估 cmets 中的答案 :)

标签: python filter lambda


【解决方案1】:

这种行为是由两件事共同造成的。其中之一是,在 Python 3 中,filter(和range)返回一次产生一个值的对象,而不是预先计算所有值。另一个是,在 Python 2 和 3 中,引用封闭作用域中名称的函数会在 names, not values 上创建闭包。

在您的 Python 3 版本中,在每次循环迭代中,您都会使用函数(您的 lambda)创建一个过滤器。因为过滤器是“惰性的”,所以它存储函数并在稍后调用它,每当您要求过滤值时。 (在这种情况下,这就是您调用list(nums) 的时候。)但是该函数引用了函数外部的变量i。因此,当filter 调用该函数时,它使用i 的值调用它,该值存在于您获取 过滤值时(即,当您调用list(nums) 时),而不是在您创建了过滤器。这就是为什么您的结果缺少所有 7 的倍数(7 除外):7 是您的 i 循环中的最后一个值,因此您的所有 lambda 在调用它们时都会检查 7 的倍数。

正如 Bhargav Rao 在评论中所说,重现 Python 2 行为的一种方法是将 lambda 更改为过滤 list(nums) 而不是 nums。这会强制每个过滤器“清除”前一个过滤器,而不是等待最后全部应用它们。 (这实际上是 Python 2 所做的,这就是您在 Python 2 中看不到这种行为的原因。)

另一种方法是使用关于闭包的链接问题中描述的默认参数技巧。将循环体更改为:

nums = filter(lambda x, i=i: x == i or x % i , list(nums))

使i 成为 lambda 的参数“锁定”每个循环迭代中的值。这意味着过滤器仍会延迟运行,但每个过滤器都会存储适当的值以进行过滤,因此即使您稍后调用它(更改 i 之后)它仍然可以工作。

【讨论】:

  • 很好的解释,这也是2to3 自动将filter(在这种情况下)更改为列表理解的原因吗?
  • @BhargavRao:可能。我不太了解 2to3 的工作原理。
  • 我猜这听起来像是另一个问题。无论如何感谢您的宝贵时间。
猜你喜欢
  • 1970-01-01
  • 2015-11-21
  • 2022-06-17
  • 2020-08-29
  • 2018-12-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多