title: generating functions in a loop using variables from the loop cause shadowing effects of variables
body: <p>I want to generate small functions in a loop that access variables from the loop. Then I want to compose and apply the functions all at once. An MWE looks lie this:</p>
  <pre><code>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) -&gt; str:
  
      def peek(x):
          print('peek:', x)
          return x
  
      replacements = [lambda txt: peek(peek(txt).replace(peek(val), f'&lt;{peek(typ)}&gt;')) 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'])
  </code></pre>
  <p>When I run this I expected to get &quot;this &lt;A&gt; a &lt;B&gt; sentence&quot;. But Only the last pair of <code>val</code> and <code>typ</code> from the loop actually are used.  So I guess some shadowing or overwriting happens. Can you explain this?</p>
  <pre><code>-&gt; % py mwe.py 
  peek: this is a test sentence
  peek: test
  peek: B
  peek: this is a &lt;B&gt; sentence
  peek: this is a &lt;B&gt; sentence
  peek: test
  peek: B
  peek: this is a &lt;B&gt; sentence
  ['is', 'test']
  ['A', 'B']
  this is a test sentence
  this is a &lt;B&gt; sentence
  </code></pre>
  <p>Btw. to isolate the issue I also wrote the function like this:</p>
  <pre><code>def replace_values_by_type(text, values, types) -&gt; str:
  
      replacements = []
  
      for val, typ in zip(values, types):
          def f(txt):
              return txt.replace(val, f'&lt;{typ}&gt;')
  
          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']))
  </code></pre>
  <p>The problem remains the same.</p>