为什么map()和列表理解的结果不同?

发布于 2024-07-06 18:03:41 字数 905 浏览 9 评论 0原文

以下测试失败:

#!/usr/bin/env python
def f(*args):
    """
    >>> t = 1, -1
    >>> f(*map(lambda i: lambda: i, t))
    [1, -1]
    >>> f(*(lambda: i for i in t)) # -> [-1, -1]
    [1, -1]
    >>> f(*[lambda: i for i in t]) # -> [-1, -1]
    [1, -1]
    """
    alist = [a() for a in args]
    print(alist)

if __name__ == '__main__':
    import doctest; doctest.testmod()

换句话说:

>>> t = 1, -1
>>> args = []
>>> for i in t:
...   args.append(lambda: i)
...
>>> map(lambda a: a(), args)
[-1, -1]
>>> args = []
>>> for i in t:
...   args.append((lambda i: lambda: i)(i))
...
>>> map(lambda a: a(), args)
[1, -1]
>>> args = []
>>> for i in t:
...   args.append(lambda i=i: i)
...
>>> map(lambda a: a(), args)
[1, -1]

The following test fails:

#!/usr/bin/env python
def f(*args):
    """
    >>> t = 1, -1
    >>> f(*map(lambda i: lambda: i, t))
    [1, -1]
    >>> f(*(lambda: i for i in t)) # -> [-1, -1]
    [1, -1]
    >>> f(*[lambda: i for i in t]) # -> [-1, -1]
    [1, -1]
    """
    alist = [a() for a in args]
    print(alist)

if __name__ == '__main__':
    import doctest; doctest.testmod()

In other words:

>>> t = 1, -1
>>> args = []
>>> for i in t:
...   args.append(lambda: i)
...
>>> map(lambda a: a(), args)
[-1, -1]
>>> args = []
>>> for i in t:
...   args.append((lambda i: lambda: i)(i))
...
>>> map(lambda a: a(), args)
[1, -1]
>>> args = []
>>> for i in t:
...   args.append(lambda i=i: i)
...
>>> map(lambda a: a(), args)
[1, -1]

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

定格我的天空 2024-07-13 18:03:42

它们是不同的,因为生成器表达式和列表 comp 中的 i 值都是延迟计算的,即在 f 中调用匿名函数时。
到那时,i 绑定到最后一个值 if t,即 -1。

基本上,这就是列表推导式的作用(对于 genexp 也是如此):

x = []
i = 1 # 1. from t
x.append(lambda: i)
i = -1 # 2. from t
x.append(lambda: i)

现在 lambda 携带一个引用 i 的闭包,但是 i 在中绑定到 -1两种情况,因为这是分配给它的最后一个值。

如果您想确保 lambda 接收 i 的当前值,请

f(*[lambda u=i: u for i in t])

这样做,您可以在创建闭包时强制对 i 求值。

编辑:生成器表达式和列表推导式之间有一个区别:后者将循环变量泄漏到周围的范围中。

They are different, because the value of i in both the generator expression and the list comp are evaluated lazily, i.e. when the anonymous functions are invoked in f.
By that time, i is bound to the last value if t, which is -1.

So basically, this is what the list comprehension does (likewise for the genexp):

x = []
i = 1 # 1. from t
x.append(lambda: i)
i = -1 # 2. from t
x.append(lambda: i)

Now the lambdas carry around a closure that references i, but i is bound to -1 in both cases, because that is the last value it was assigned to.

If you want to make sure that the lambda receives the current value of i, do

f(*[lambda u=i: u for i in t])

This way, you force the evaluation of i at the time the closure is created.

Edit: There is one difference between generator expressions and list comprehensions: the latter leak the loop variable into the surrounding scope.

山川志 2024-07-13 18:03:42

lambda 捕获变量,而不是值,因此代码

lambda : i

将始终返回闭包中当前绑定到的值 i。 当它被调用时,该值已被设置为-1。

为了得到你想要的,你需要在创建 lambda 时捕获实际的绑定,方法是:

>>> f(*(lambda i=i: i for i in t)) # -> [-1, -1]
[1, -1]
>>> f(*[lambda i=i: i for i in t]) # -> [-1, -1]
[1, -1]

The lambda captures variables, not values, hence the code

lambda : i

will always return the value i is currently bound to in the closure. By the time it gets called, this value has been set to -1.

To get what you want, you'll need to capture the actual binding at the time the lambda is created, by:

>>> f(*(lambda i=i: i for i in t)) # -> [-1, -1]
[1, -1]
>>> f(*[lambda i=i: i for i in t]) # -> [-1, -1]
[1, -1]
零時差 2024-07-13 18:03:42

表达式 f = lambda: i 等效于:

def f():
    return i

表达式 g = lambda i=i: i 等效于:

def g(i=i):
    return i

i 是一个 自由变量 在第一种情况下,它绑定到第二种情况下的函数参数,即,它在这种情况下是一个局部变量。 默认参数的值在函数定义时评估。

生成器表达式是 lambda 表达式中 i 名称最近的封闭范围(其中定义了 i),因此 i > 在该块中解析:

f(*(lambda: i for i in (1, -1)) # -> [-1, -1]

ilambda i: ... 块的局部变量,因此它引用的对象是在该块中定义的:

f(*map(lambda i: lambda: i, (1,-1))) # -> [1, -1]

Expression f = lambda: i is equivalent to:

def f():
    return i

Expression g = lambda i=i: i is equivalent to:

def g(i=i):
    return i

i is a free variable in the first case and it is bound to the function parameter in the second case i.e., it is a local variable in that case. Values for default parameters are evaluated at the time of function definition.

Generator expression is the nearest enclosing scope (where i is defined) for i name in the lambda expression, therefore i is resolved in that block:

f(*(lambda: i for i in (1, -1)) # -> [-1, -1]

i is a local variable of the lambda i: ... block, therefore the object it refers to is defined in that block:

f(*map(lambda i: lambda: i, (1,-1))) # -> [1, -1]
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文