42

From the title, yes there is a difference. Now applied to my scenario: let's consider a class Dummy:

class Dummy:
    def __init__(self):
        self.attached = []

    def attach_item(self, item):
        self.attached.append(item)

If I use this:

D = Dummy()
items = [1, 2, 3, 4]
for item in items:
    D.attach_item(item)

I indeed get D.attached = [1, 2, 3, 4]. But if I map the function attach_item to the items, D.attached remains empty.

map(D.attach_item, items)

What is it doing?

Anonymous
  • 11,740
  • 3
  • 40
  • 50
Mathieu
  • 5,410
  • 6
  • 28
  • 55
  • 3
    See [When should I use a Map instead of a For Loop?](//stackoverflow.com/q/1975250). – Aran-Fey Aug 03 '18 at 11:20
  • Possible duplicate of [Print doesn't print when it's in map, Python](https://stackoverflow.com/questions/7731213/print-doesnt-print-when-its-in-map-python) – user202729 Aug 04 '18 at 02:48
  • Another duplicates https://stackoverflow.com/questions/10973766/understanding-the-map-function and https://stackoverflow.com/questions/16750493/python-map-function-iteration and https://stackoverflow.com/questions/47998941/is-the-python-map-function-a-value-returning-function and related https://stackoverflow.com/questions/245792/when-is-not-a-good-time-to-use-python-generators – Mazdak Aug 04 '18 at 11:51

3 Answers3

55

A very interesting question which has an interesting answer.

The map function returns a Map object which is iterable. map is performing its calculation lazily so the function wouldn't get called unless you iterate that object.

So if you do:

x = map(D.attach_item, items)
for i in x:
    continue

The expected result will show up.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Farbod Shahinfar
  • 777
  • 6
  • 15
  • 8
    same if you list(x) – joaquin Aug 03 '18 at 11:08
  • @joaquin That's where I got tricked. In my mind, when doing `list(x)`, you want to convert `x` into a list. – Mathieu Aug 03 '18 at 11:11
  • 6
    @Mathieu You do. The result will be a list full of None. But as a side effect the function will have modified the internal state of the Dummy object, which is what you want. In general, lazy evaluation is not very intuitive when you are more interested in the side effect than in the actual result – Ant Aug 03 '18 at 13:49
  • 10
    @Ant And for that reason I'd argue using `map` for side effects is bad programming style. It's a confusing way to do it, and the OP's question shows this confusion. – marcelm Aug 03 '18 at 17:37
16

map only creates an iterator. You should iterate through it to add items into D.attached. Like this:

D = Dummy()
items = [1, 2, 3, 4]
list(map(D.attach_item, items))

Yep, don't do it in your code:) But the example is just useful for understanding.

Lev Zakharov
  • 2,409
  • 1
  • 10
  • 24
  • 1
    Should have just read the doc x') I thought I knew what map was doing, turn out I was wrong... Thanks! – Mathieu Aug 03 '18 at 11:06
9

Quoting the documentation

Return an iterator that applies function to every item of iterable, yielding the results.

which means you have to collect the iterator, e.g.

list(map(D.attach_item, items))

> [None, None, None, None]

Hmmm, strange. Why None, None, ...

Yes, you can convert any loop in a map statement, but it's not always useful. Map takes a parameter and does something with it (in most cases) an returns it, without side effects! Here's an example:

def add(a):
    return a + 3
list(map(add, items))

> [4, 5, 6, 7]

The true power comes, when you combine it with other functions like filter

def add(a):
    return a + 3
def odd(a):
    return a % 2 == 1
list(map(add, filter(odd, items)))

> [4, 6]
hellow
  • 12,430
  • 7
  • 56
  • 79
  • When I was using map previously it was always in scenarios where I needed to catch the return from the function (i.e. your example). Thus I was applying `list()`. I didn' t pay attention to the fact that map just returning an iterator :/ – Mathieu Aug 03 '18 at 11:10