12

次のテストは失敗します。

#!/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]
4

3 に答える 3

9

iジェネレーター式とリスト comp の両方の値が遅延評価されるため、つまり、無名関数が で呼び出されるときに、それらは異なり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)

現在、ラムダは を参照するクロージャーを持ち歩いていますii、どちらの場合も -1 にバインドされています。これが最後に割り当てられた値であるためです。

ラムダが の現在の値を確実に受け取るようにしたい場合はi、次のようにします。

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

iこのようにして、クロージャの作成時にの評価を強制します。

編集: ジェネレーター式とリスト内包表記には 1 つの違いがあります。後者は、ループ変数を周囲のスコープにリークします。

于 2008-09-26T14:31:47.577 に答える
6

ラムダは値ではなく変数をキャプチャするため、コード

lambda : i

クロージャで i が現在バインドされている値を常に返します。呼び出されるまでに、この値は -1 に設定されています。

必要なものを取得するには、ラムダが作成された時点で、次の方法で実際のバインディングをキャプチャする必要があります。

>>> 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]
于 2008-09-26T14:28:40.580 に答える
4

f = lambda: iは次と同等です。

def f():
    return i

g = lambda i=i: iは次と同等です。

def g(i=i):
    return i

i最初のケースでは自由変数であり、2 番目のケースでは関数パラメータにバインドされています。つまり、そのケースではローカル変数です。デフォルト パラメーターの値は、関数の定義時に評価されます。

ジェネレーター式は、式内のnameの最も近い囲みスコープ (iが定義されている場所) であるため、そのブロックで解決されます。ilambdai

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

ilambda i: ...ブロックのローカル変数であるため、それが参照するオブジェクトはそのブロックで定義されています。

f(*map(lambda i: lambda: i, (1,-1))) # -> [1, -1]
于 2008-09-26T18:24:31.977 に答える