使用循环中的变量在循环中生成函数会导致变量的阴影效应

generating functions in a loop using variables from the loop cause shadowing effects of variables

我想在循环中生成从循环中访问变量的小函数。然后我想一次组合并应用所有功能。 MWE 看起来是这样说的:

from functools import reduce

def compose(*funcs):
    return lambda x: reduce(lambda y, f: f(y), reversed(funcs), x)


def replace_values_by_type(text, values, types) -> str:

    def peek(x):
        print('peek:', x)
        return x

    replacements = [lambda txt: peek(peek(txt).replace(peek(val), f'<{peek(typ)}>')) for val, typ in zip(values, types)]
    replace = compose(*replacements)

    text_with_replacements = replace(text)

    print(values)
    print(types)
    print(text)
    print(text_with_replacements)
    print()

    return text_with_replacements



replace_values_by_type('this is a test sentence', ['is', 'test'], ['A', 'B'])

当我 运行 我希望得到“this a sentence”。但是实际上只使用了循环中的最后一对 valtyp 。所以我猜会发生一些阴影或覆盖。你能解释一下吗?

-> % py mwe.py 
peek: this is a test sentence
peek: test
peek: B
peek: this is a <B> sentence
peek: this is a <B> sentence
peek: test
peek: B
peek: this is a <B> sentence
['is', 'test']
['A', 'B']
this is a test sentence
this is a <B> sentence

顺便说一句。为了隔离问题,我还编写了这样的函数:

def replace_values_by_type(text, values, types) -> str:

    replacements = []

    for val, typ in zip(values, types):
        def f(txt):
            return txt.replace(val, f'<{typ}>')

        replacements.append(f)

    text_with_replacements = text
    for f in replacements:
        text_with_replacements = f(text_with_replacements)

    return text_with_replacements


print(replace_values_by_type('this is a test sentence', ['is', 'test'], ['A', 'B']))

问题依旧。

列表理解创建的所有闭包都在同一个变量范围内,循环中只有一个 valtyp 变量实例。当稍后调用闭包时,变量具有上次迭代的值。

您需要为每次迭代在唯一范围内生成闭包。一种方法是编写一个单独的函数来 returns 闭包,因为每个函数都会建立一个新的范围。

def replacer(val, typ):
    return lambda txt: peek(peek(txt).replace(peek(val), f'<{peek(typ)}>'))

replacements = [replacer(val, typ) for val, typ in zip(values, types)]