Why results of map() and list comprehension are different? [duplicate]

The following test fails:

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

if __name__ == '__main__':
    import doctest; doctest.testmod()

In other words:

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

Asked by: Patrick869 | Posted: 24-09-2021

Answer 1

They are different, because the value of i in both the generator expression and the list comp are evaluated lazily, i.e. when the anonymous functions are invoked in f.
By that time, i is bound to the last value if t, which is -1.

So basically, this is what the list comprehension does (likewise for the genexp):

x = []
i = 1 # 1. from t
x.append(lambda: i)
i = -1 # 2. from t
x.append(lambda: i)

Now the lambdas carry around a closure that references i, but i is bound to -1 in both cases, because that is the last value it was assigned to.

If you want to make sure that the lambda receives the current value of i, do

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

This way, you force the evaluation of i at the time the closure is created.

Edit: There is one difference between generator expressions and list comprehensions: the latter leak the loop variable into the surrounding scope.

Answered by: Blake806 | Posted: 25-10-2021

Answer 2

The lambda captures variables, not values, hence the code

lambda : i

will always return the value i is currently bound to in the closure. By the time it gets called, this value has been set to -1.

To get what you want, you'll need to capture the actual binding at the time the lambda is created, by:

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

Answered by: Kelvin440 | Posted: 25-10-2021

Answer 3

Expression f = lambda: i is equivalent to:

def f():
    return i

Expression g = lambda i=i: i is equivalent to:

def g(i=i):
    return i

i is a free variable in the first case and it is bound to the function parameter in the second case i.e., it is a local variable in that case. Values for default parameters are evaluated at the time of function definition.

Generator expression is the nearest enclosing scope (where i is defined) for i name in the lambda expression, therefore i is resolved in that block:

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

i is a local variable of the lambda i: ... block, therefore the object it refers to is defined in that block:

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

Answered by: Darcy210 | Posted: 25-10-2021

Similar questions

python - Why does list comprehension using a zip object results in an empty list?

f = lambda x : 2*x g = lambda x : x ** 2 h = lambda x : x ** x funcTriple = ( f, g, h ) myZip = ( zip ( funcTriple, (1, 3, 5) ) ) k = lambda pair : pair[0](pair[1]) # Why do Output # 1 (2, 9, 3125) and Output # 2 ( [ ] ) differ? print ("\n\nOutput # 1: for pair in myZip: k(pair) ...") for pair in myZip : print ( k(pair) ) print ("\n\nOutput # 2: [ k(pair) for pair in myZip ] ...") print ( [ k(pair) for pai...

Using list comprehension in Python to do something similar to zip()?

I'm a Python newbie and one of the things I am trying to do is wrap my head around list comprehension. I can see that it's a pretty powerful feature that's worth learning. cities = ['Chicago', 'Detroit', 'Atlanta'] airports = ['ORD', 'DTW', 'ATL'] print zip(cities,airports) [('Chicago', 'ORD'), ('Detroit', 'DTW'), ('Atlanta', 'ATL')] How do I use list comprehension so I can get the resul...

python - How do I make this simple list comprehension?

I'm new to python, and I'm trying to get to know the list comprehensions better. I'm not even really sure if list comprehension is the word I'm looking for, since I'm not generating a list. But I am doing something similar. This is what I am trying to do: I have a list of numbers, the length of which is divisible by three. So say I have nums = [1, 2, 3, 4, 5, 6] I want to iterate ov...

python - List comprehension for series of deltas

How would you write a list comprehension in python to generate a series of n-1 deltas between n items in an ordered list? Example: L = [5,9,2,1,7] RES = [5-9,9-2,2-1,1-7] = [4,7,1,6] # absolute values

Map vs list comprehension in Python

This question already has answers here:

python - Is it possible to use 'else' in a list comprehension?

This question already has answers here:

python - Why doesn't this list comprehension do what I expect it to do?

The original list project_keys = sorted(projects.keys()) is [101, 102, 103, 104, 105, 106, 107, 108, 109, 110] where the following projects were deemed invalid this year: 108, 109, 110. Thus: for project in projects.itervalues(): # The projects dictionary is mapped to the Project class if project.invalid: # Where invalid is a Bool parameter in the Pro...

python - List comprehension vs. lambda + filter

I have a list that I want to filter by an attribute of the items. Which of the following is preferred (readability, performance, other reasons)? xs = [x for x in xs if x.attribute == value] xs = filter(lambda x: x.attribute == value, xs)

python - Better Way to Write This List Comprehension?

I'm parsing a string that doesn't have a delimiter but does have specific indexes where fields start and stop. Here's my list comprehension to generate a list from the string: field_breaks = [(0,2), (2,10), (10,13), (13, 21), (21, 32), (32, 43), (43, 51), (51, 54), (54, 55), (55, 57), (57, 61), (61, 63), (63, 113), (113, 163), (163, 213), (213, 238), (238, 240), (240, 250), (250, 300)] s = '4100100297LICACT...

python - Nested For Loops Using List Comprehension

If I had two strings, 'abc' and 'def', I could get all combinations of them using two for loops: for j in s1: for k in s2: print(j, k) However, I would like to be able to do this using list comprehension. I've tried many ways, but have never managed to get it. Does anyone know how to do this?

In Python, is there a concise way to use a list comprehension with multiple iterators?

Basically, I would like to build a list comprehension over the "cartesian product" of two iterators. Think about the following Haskell code: [(i,j) | i <- [1,2], j <- [1..4]] which yields [(1,1),(1,2),(1,3),(1,4),(2,1),(2,2),(2,3),(2,4)] Can I obtain a similar behavi...

python: list comprehension tactics

I'm looking to take a string and create a list of strings that build up the original string. e.g.: "asdf" => ["a", "as", "asd", "asdf"] I'm sure there's a "pythonic" way to do it; I think I'm just losing my mind. What's the best way to get this done?

How to make a list comprehension with the group() method in python?

I'm trying to write a little script to clean my directories. In fact I have: pattern = re.compile(format[i]) ... current_f.append(pattern.search(str(ls))) and I want to use a list comprehension but when I try: In [25]: [i for i in current_f.group(0)] I get: AttributeError: 'list' object has no attribute 'group' So how to m...

Python list comprehension - access last created element?

Is it possible to access the previous element generated in a list comprehension. I am working on some toy encryption stuff. Given the key as an arbitrarily large integer, an initialization value, and a list of elements as the message to encrypt. I need to xor each element with the previous ciphered element and the key. The following loop would do. previous = initialization_value cipher = [] for e...

heap - Does Python's heapify() not play well with list comprehension and slicing?

I found an interesting bug in a program that I implemented somewhat lazily, and wondered if I'm comprehending it correctly. The short version is that Python's heapq implementation doesn't actually order a list, it merely groks the list in a heap-centric way. Specifically, I was expecting heapify() to result in an ord...

list comprehension - One liner to replicate lines coming from a file (Python)

I have a regular list comprehension to load all lines of a file in a list f = open('file') try: self._raw = [L.rstrip('\n') for L in f] finally: f.close() Now I'd like to insert in the list each line 'n' times on the fly. How to do it inside the list comprehension ? Tnx

python - Why does list comprehension using a zip object results in an empty list?

f = lambda x : 2*x g = lambda x : x ** 2 h = lambda x : x ** x funcTriple = ( f, g, h ) myZip = ( zip ( funcTriple, (1, 3, 5) ) ) k = lambda pair : pair[0](pair[1]) # Why do Output # 1 (2, 9, 3125) and Output # 2 ( [ ] ) differ? print ("\n\nOutput # 1: for pair in myZip: k(pair) ...") for pair in myZip : print ( k(pair) ) print ("\n\nOutput # 2: [ k(pair) for pair in myZip ] ...") print ( [ k(pair) for pai...

python - How to walk up a linked-list using a list comprehension?

I've been trying to think of a way to traverse a hierarchical structure, like a linked list, using a list expression, but haven't come up with anything that seems to work. Basically, I want to convert this code: p = self.parent names = [] while p: names.append(p.name) p = p.parent print ".".join(names) into a one-liner like: print ".".join( [o.name for o in <...

Perl equivalent of (Python-) list comprehension

I'm looking for ways to express this Python snippet in Perl: data = {"A": None, "B": "yes", "C": None} key_list = [k for k in data if data[k]] # in this case the same as filter(lambda k: data[k], data) but let's ignore that So looking at it one way, I just want the keys where the values are None or undef. Looking at it another way, what I want is the concise perl equiva...

python - Double Iteration in List Comprehension

In Python you can have multiple iterators in a list comprehension, like [(x,y) for x in a for y in b] for some suitable sequences a and b. I'm aware of the nested loop semantics of Python's list comprehensions. My question is: Can one iterator in the comprehension refer to the other? In other words: Could I have something like this: [x for x in a for a in b] ...

Still can't find your answer? Check out these communities...

PySlackers | Full Stack Python | NHS Python | Pythonist Cafe | Hacker Earth | Discord Python