How to understand mapping (map) of functions list in Python?

Asked

Viewed 80 times

7

lista = [0, 1, 2, 3, 4]

def square(y):
    return (y**2)
    
def cube(y):
    return (y**3)

funcs = [square, cube]

for i in lista:
    valor = map(lambda x: x(i), funcs)
    print(list((valor)))

Exit:

[0, 0]
[1, 1]
[4, 8]
[9, 27]
[16, 64]

I can’t understand the function map is receiving a list of functions in your second argument.

Until now I had only seen list of values in this argument. And in the lambda function, the x(i) is passing each element of the list as argument for each function in the list funcs (which has each element a defined function).

This example is very different from the one I had learned about function map.

Can anyone explain how to interpret this code?

2 answers

7

Until now I had only seen list of values in this argument.

There is no difference because, in a way, a list of functions is still a list of values. The logic is the same, let’s recap.

The more general purpose of map is to make a mapping over a container. In Python, the function built-in map maps each element of an iterable (container) to a given function. This iterable can be a list, tuple, etc.

This means that, to each one of the values of the iterable (a list, for example) provided in the second argument, the mapping function will be applied, generating a new list of same length, but with the values changed according to the mapping function.

With this definition, let’s return to the question code:

lista = [0, 1, 2, 3, 4]

def square(y):
        return (y**2)
    
def cube(y):
        return (y**3)

funcs = [square, cube]

for i in lista:
    valor = map(lambda x: x(i), funcs)
    print(list(valor))

Like the map is inside a for, it will be executed several times. In this specific case, it will be executed map for each of the items on the list lista.

To analyze the map, we can study the case of only one of the iterations. Let’s see what happens in the third iteration (where the value of i is 2):

# `i` está definido como `2`.
valor = map(lambda x: x(i), funcs)
print(list(valor))

We will provide the map the eternal funcs, which is basically a list of functions. For each of the functions in this list, we will perform the past mapping function.

In this case, this mapping function is basically to apply the current mapping function to the value i.

So, i being 2 (third iteration), map shall produce an iterable corresponding to:

[square(2), cube(2)]
# Que é avaliado para: [4, 8]

Perhaps the difficulty of understanding the map has come from the fact that a mapping is done to each of the iterations of for external. In this case, by analyzing only one of the iterations we can have a better notion than, in fact, the map is making.

With this understanding, we can expand to a higher level of code: mapping the functions of funcs shall be made for each of the elements of lista.


Another thing that can help you understand is to make a table test, but then I think it’s already beyond the scope of this answer.

3

Let’s start from the beginning. According to documentation:

Functions are first-class Objects.

That is, in Python the functions are "first-class citizens": they are treated as if they were "normal" values, can be placed in lists, assigned to a variable, passed as parameter to other functions, etc. On this subject, you can read more here, here and here.


Therefore, a function can be assigned to a variable:

def square(y):
        return y ** 2
    
def cube(y):
        return y ** 3

# atribui a função a uma variável
funcao = square
# como "funcao" aponta para "square", posso chamá-la normalmente
print(funcao(3)) # 9

# atribui outra função na mesma variável
funcao = cube
# agora "funcao" aponta para "cube"
print(funcao(3)) # 27

And I can also create a list of various functions, scroll through that list in one loop and call them all:

def square(y):
        return y ** 2
    
def cube(y):
        return y ** 3

# lista contendo as funções
funcs = [square, cube]
# para cada função da lista, chamá-la passando o número 3 como argumento
for funcao in funcs:
    print(f'chamando {funcao.__name__}: {funcao(3)}')

The output of this code is:

chamando square: 9
chamando cube: 27

Now about the map: all he does is apply a function to all the elements of an eternal. That is, if I do:

def dobro(n):
    return n * 2

valores = [1, 2, 3]
for result in map(dobro, valores):
    print(result)

This will print 2, 4 and 6, for map(dobro, valores) apply the function dobro to each of the elements on the list valores. Essentially, this would be equivalent to:

for valor in valores:
    print(dobro(valor))

But in your case the list has no values that are passed to a function. What it has are functions that will be called with a certain value. I mean, it would be like doing this:

# para cada função da lista "funcs", chamá-la passando o "3" como argumento
for funcao in funcs:
    print(funcao(3))

And in this case, the equivalent using map would have to be:

for result in map(lambda funcao: funcao(3), funcs):
    print(result)

That is, the function that map receives is a lambda (that in the background is also a function), which takes as argument a function and the flame (passing, in this case, the number 3 as argument). That is, instead of myself calling the function directly within the loop, is the map that the flame and returns the result (so within the for I can already catch the direct result).


But in your case you’re not iterating for map, and yes taking his result and passing to list, which in turn creates a list containing all the results. That is, when you do:

valor = map(lambda funcao: funcao(3), funcs)
print(list(valor)) # [9, 27]

Would be equivalent to doing:

results = [] # cria uma lista vazia
# para cada resultado do map, adicioná-lo na lista
for result in map(lambda funcao: funcao(3), funcs):
    results.append(result)
print(results) # [9, 27]

And finally, in your code all this is done inside a loop, iterating by list values [0, 1, 2, 3, 4]. In the above examples I always passed the number 3 for the duties, but in its loop he does it for all values on the list.

That is, first he calls the functions to the 0 and print the list with the results, then do the same for the 1, to the 2 etc..


And just for the record, another way to create this list of results would be to use a comprehensilist on:

for i in lista:
    print([ funcao(i) for funcao in funcs ])

Browser other questions tagged

You are not signed in. Login or sign up in order to post.