1

The following code does not print anything from the for loop; it only prints the result of the tuple. This is very unintuitive because I haven't explicitly modified the cheapest map, yet it behaves as if it is empty after calling tuple.

So, what's going on?

store1 = [10.00, 11.00, 12.34, 2.34]
store2 = [9.00, 11.10, 12.34, 2.01]
cheapest = map(min, store1, store2)

print(tuple(cheapest))

for v in cheapest: 
    print(v) 

Expected output:

(9.0, 11.0, 12.34, 2.01)
9.0
11.0
12.34
2.01

Actual output:

(9.0, 11.0, 12.34, 2.01)

If I run the for loop first, then the behavior is reversed: the for loop prints the values from the cheapest map and the print(tuple(cheapest)) result is empty as in:

Actual output (order reversed):

9.0
11.0
12.34
2.01
()
Austin
  • 8,018
  • 2
  • 31
  • 37
  • 1
    It's not emptied so much as that iterator is just consumed to create the tuple, that's how it gets data. If you want to preserve it, consider `list(map(...))` – tadman Dec 29 '22 at 00:33
  • So... in Java `map` is a data structure, but in Python it's just an iterator? *oof* – Austin Dec 29 '22 at 00:36
  • It's just how Python rolls. This way if you didn't need all of the results, it wouldn't bother computing them, which is more efficient. – tadman Dec 29 '22 at 00:36
  • Thanks @tadman Makes sense! Just need to rewire my brain then. :) – Austin Dec 29 '22 at 00:38
  • 1
    @BrokenBenchmark saved; that's probably better than other dupe targets I might have found for the general problem (which also commonly comes up with file iterators). – Karl Knechtel Dec 29 '22 at 00:45

1 Answers1

1

When learning Python you'll need to pay close attention to the return type of various functions, as many, like map, actually return an iterator1.

While an iterator can be efficient as it is only evaluated if necessary, it can lead to confusion like this where your data is suddenly "gone".

If you need to capture that result before using it in multiple locations, convert it into a more permanent form, like:

cheapest = list(map(min, store1, store2))

Where that can still be used in a for since it's iterable.


1 In Ruby this is like map { ... }.lazy being enabled by default. In Rust it's very similar to Python, a map() must be captured with collect() before it's anything other than an iterator. If you're used to things like JavaScript where it just dumps out an array, it'll require a bit of re-learning.

tadman
  • 208,517
  • 23
  • 234
  • 262