首页 > 解决方案 > 使用循环中的变量在循环中生成函数会导致变量的阴影效应

问题描述

我想在循环中生成从循环访问变量的小函数。然后我想一次编写和应用所有功能。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> a <B> sentence”。但实际上只使用了最后一对valand from 循环。typ所以我猜会发生一些阴影或覆盖。你能解释一下吗?

-> % 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']))

问题仍然相同。

标签: pythonvariablesscopecomposition

解决方案


列表推导式创建的所有闭包都在同一个变量范围内,并且循环只有一个valand变量实例。typ稍后调用闭包时,变量具有上次迭代的值。

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

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)]

推荐阅读