On Thu, 30 Jun 2022 at 02:49, Johannes Bauer <dfnsonfsdu...@gmx.de> wrote: > But now consider what happens when we create the lambdas inside a list > comprehension (in my original I used a generator expresison, but the > result is the same). Can you guess what happens when we create conds > like this? > > conds = [ lambda msg: msg.hascode(z) for z in ("foo", "bar") ] > > I certainly could not. Here's what it outputs: > > Check for bar > False > Check for bar > False > > I.e., the iteration variable "z" somehow gets bound inside the lambda > not by its value, but by its reference. All checks therefore refence > only the last variable. >
Yep, that is the nature of closures. (Side point: This isn't actually a generator expression, it's a list comprehension; current versions of Python treat them broadly the same way, but there was previously a difference in the way scoping worked.) What you're seeing is a consequence of the way that closures work, and it is a very good thing most of the time :) The usual way to "snapshot" a variable is what you showed in your followup: a default argument value. def f(..., z=z): ... z has been snapshot (As others have pointed out, this isn't unique to lambdas; any function will behave that way.) Antoon offered another variant, but written as a pair of lambda functions, it's a little hard to see what's going on. Here's the same technique written as a factory function: def does_it_have(z): return lambda msg: msg.hascode(z) conds = [does_it_have(z) for z in ("foo", "bar")] Written like this, it's clear that the variable z in the comprehension is completely different from the one inside does_it_have(), and they could have different names if you wanted to. This is a fairly clean way to snapshot too, and has the advantage that it doesn't pretend that the function takes an extra parameter. ChrisA -- https://mail.python.org/mailman/listinfo/python-list