You could make your own "each" function:
def each(fn, items):
for item in items:
fn(item)
# called thus
each(lambda x: installWow(x, 'installed by me'), wowList)
Basically it's just map, but without the results being returned. By using a function you'll ensure that the "item" variable doesn't leak into the current scope.
Answer from John Montgomery on Stack OverflowYou could make your own "each" function:
def each(fn, items):
for item in items:
fn(item)
# called thus
each(lambda x: installWow(x, 'installed by me'), wowList)
Basically it's just map, but without the results being returned. By using a function you'll ensure that the "item" variable doesn't leak into the current scope.
You can use the built-in any function to apply a function without return statement to any item returned by a generator without creating a list. This can be achieved like this:
any(installWow(x, 'installed by me') for x in wowList)
I found this the most concise idom for what you want to achieve.
Internally, the installWow function does return None which evaluates to False in logical operations. any basically applies an or reduction operation to all items returned by the generator, which are all None of course, so it has to iterate over all items returned by the generator. In the end it does return False, but that doesn't need to bother you. The good thing is: no list is created as a side-effect.
Note that this only works as long as your function returns something that evaluates to False, e.g., None or 0. If it does return something that evaluates to True at some point, e.g., 1, it will not be applied to any of the remaining elements in your iterator. To be safe, use this idiom mainly for functions without return statement.
What you are seeing here is the effect of lazy-evaluation. Python 3 made most functions like this (map, filter, zip etc) work lazily where they used to work eagerly in Python 2, that is, instead of immediately returning and materializing a data-structure when you call map(f, some_iterable), instead, map returns a map-object, which can then be iterated over to either materialize a data structure or work with the elements one-by-one (letting you work in a memory-efficient way).
>>> result = []
>>> m = map(lambda x: result.append(x), range(10))
>>> m
<map object at 0x10a0b7278>
>>> result
[]
>>> next(m)
>>> result
[0]
>>> next(m)
>>> result
[0, 1]
>>> list(m)
[None, None, None, None, None, None, None, None]
>>> result
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
However!
You should not be using map or filter or list-comprehensions for their side-effects. These are all functional constructs, and you should avoid mutating state when using them. It is simply bad style, and also, as you noticed when I called list on my map object, it creates and immediately discards a useless list of None (because functions that don't return anything implicitely return None).
So, to answer your question, yes you can use "map() for functions that does not return a value" but you shouldn't. Just use a for-loop.
map creates an iterator. It doesn't do the mapping immediately, it does it one by one as you consume the iterator. list(map(...)) immediately consumes the entire iterator to turn it into a list. But in your tests where it doesn't work, you're never consuming the iterator, so f never gets called even once.
In other words: doesn't work:
map(f, a)
print(result)
Works:
list(map(f, a)) # ← list consumes the iterator
print(result)
You cannot assign in a lambda, but lambda is just shorthand for a function so you could:
def set_it(x):
x.attribute = new_value
map(set_it, li)
compared with the obvious:
for x in li:
x.attribute = new_value
A general rule of thumb for map vs a for loop (whether a list comprehension or written out in full) is that map may be faster if and only if it doesn't call a function written in Python. So expect that map will be slower in this case. Also the straight for loop doesn't create and then throw away an unwanted intermediate list so expect the map to lose out even more than usual.
Your approach won't work. It is not possible to assign a value in a lambda construct.
I think the reason why map still exists at all when generator expressions also exist, is that it can take multiple iterator arguments that are all looped over and passed into the function:
>>> list(map(min, [1,2,3,4], [0,10,0,10]))
[0,2,0,4]
That's slightly easier than using zip:
>>> list(min(x, y) for x, y in zip([1,2,3,4], [0,10,0,10]))
Otherwise, it simply doesn't add anything over generator expressions.
Because it returns an iterator, it omit storing the full size list in the memory. So that you can easily iterate over it in the future not making any pain to memory. Possibly you even don't need a full list, but the part of it, until your condition is reached.
You can find this docs useful, iterators are awesome.
An object representing a stream of data. Repeated calls to the iterator’s
__next__()method (or passing it to the built-in functionnext()) return successive items in the stream. When no more data are available aStopIterationexception is raised instead. At this point, the iterator object is exhausted and any further calls to its__next__()method just raiseStopIterationagain. Iterators are required to have an__iter__()method that returns the iterator object itself so every iterator is also iterable and may be used in most places where other iterables are accepted. One notable exception is code which attempts multiple iteration passes. A container object (such as alist) produces a fresh new iterator each time you pass it to theiter()function or use it in a for loop. Attempting this with an iterator will just return the same exhausted iterator object used in the previous iteration pass, making it appear like an empty container.
map cannot directly filter out items. It outputs one item for each item of input. You can use a list comprehension to filter out None from your results.
r = [x for x in map(calc, range(1,10)) if x is not None]
(This only calls calc once on each number in the range.)
Aside: there is no need to write lambda num: calc(num). If you want a function that returns the result of calc, just use calc itself.
Not when using map itself, but you can change your map() call to:
r = [calc(num) for num in range(1, 10) if calc(num) is not None]
print(r) # no need to wrap in list() anymore
to get the result you want.