Get started learning Python with DataCamp's free Intro to Python tutorial. Learn Data Science by completing interactive coding challenges and watching videos by expert instructors. Start Now!
This site is generously supported by DataCamp. DataCamp offers online interactive Python Tutorials for Data Science. Join 11 million other learners and get started learning Python for data science today!
Good news! You can save 25% off your Datacamp annual subscription with the code LEARNPYTHON23ALE25 - Click here to redeem your discount
Mapa, Filtro, Reduzir
Map, Filter e Reduce são paradigmas da programação funcional. Eles permitem que o programador (você) escreva um código mais simples e curto, sem necessariamente precisar se preocupar com detalhes como loops e ramificações.
Essencialmente, essas três funções permitem que você aplique uma função em vários iteráveis de uma só vez. map
e filter
vêm embutidos no Python (no módulo __builtins__
) e não requerem importação. reduce
, no entanto, precisa ser importado, pois reside no módulo functools
. Vamos entender melhor como todos funcionam, começando com map
.
Map
A função map()
em Python tem a seguinte sintaxe:
map(func, *iterables)
Onde func
é a função na qual cada elemento em iterables
(quantos forem) será aplicado. Percebe o asterisco (*
) em iterables
? Isso significa que pode haver tantos iteráveis quanto possível, desde que func
tenha exatamente esse número como argumentos de entrada exigidos. Antes de passarmos a um exemplo, é importante que você note o seguinte:
- No Python 2, a função
map()
retorna uma lista. No Python 3, no entanto, a função retorna ummap object
, que é um objeto gerador. Para obter o resultado como uma lista, a função embutidalist()
pode ser chamada no objeto map. Ou seja,list(map(func, *iterables))
- O número de argumentos para
func
deve ser o número deiterables
listados.
Vamos ver como essas regras funcionam com os seguintes exemplos.
Digamos que eu tenha uma lista (iterable
) dos meus nomes de animais de estimação favoritos, todos em minúsculas, e eu precise deles em maiúsculas. Tradicionalmente, em programação normal em Python, eu faria algo assim:
my_pets = ['alfred', 'tabitha', 'william', 'arla']
uppered_pets = []
for pet in my_pets:
pet_ = pet.upper()
uppered_pets.append(pet_)
print(uppered_pets)
O que geraria como saída ['ALFRED', 'TABITHA', 'WILLIAM', 'ARLA']
Com as funções map()
, não só é mais fácil, mas também muito mais flexível. Eu simplesmente faço isso:
# Python 3
my_pets = ['alfred', 'tabitha', 'william', 'arla']
uppered_pets = list(map(str.upper, my_pets))
print(uppered_pets)
O que também resultaria no mesmo resultado. Note que, utilizando a sintaxe definida do map()
, func
neste caso é str.upper
e iterables
é a lista my_pets
-- apenas um iterável. Também note que nós não chamamos a função str.upper
(fazendo isso: str.upper()
), já que a função map faz isso por nós em cada elemento da lista my_pets
.
O que é mais importante notar é que a função str.upper
exige apenas um argumento por definição e, portanto, passamos apenas um iterável para ela. Assim, se a função que você está passando requer dois, ou três, ou n argumentos, então você precisa passar dois, três ou n iteráveis para ela. Deixe-me esclarecer isso com outro exemplo.
Digamos que eu tenha uma lista de áreas de círculos que eu calculei em algum lugar, todas com cinco casas decimais. E eu preciso arredondar cada elemento da lista para o número correspondente de casas decimais, o que significa que eu tenho que arredondar o primeiro elemento da lista para uma casa decimal, o segundo elemento da lista para duas casas decimais, o terceiro elemento da lista para três casas decimais, e assim por diante. Com map()
isto é muito fácil. Vamos ver como.
Python já nos abençoa com a função embutida round()
que leva dois argumentos -- o número a ser arredondado e o número de casas decimais para arredondar o número. Então, como a função requer dois argumentos, precisamos passar dois iteráveis.
# Python 3
circle_areas = [3.56773, 5.57668, 4.00914, 56.24241, 9.01344, 32.00013]
result = list(map(round, circle_areas, range(1, 7)))
print(result)
Vê a beleza do map()
? Consegue imaginar a flexibilidade que isso evoca?
A função range(1, 7)
atua como o segundo argumento para a função round
(o número de casas decimais necessárias por iteração). Então, à medida que map
itera através de circle_areas
, durante a primeira iteração, o primeiro elemento de circle_areas
, 3.56773
, é passado junto com o primeiro elemento de range(1,7)
, 1
, para round
, tornando-o efetivamente round(3.56773, 1)
. Durante a segunda iteração, o segundo elemento de circle_areas
, 5.57668
, junto com o segundo elemento de range(1,7)
, 2
, é passado para round
, tornando-se round(5.57668, 2)
. Isso acontece até que o final da lista circle_areas
seja alcançado.
Tenho certeza de que você está se perguntando: "E se eu passar um iterável menor ou maior que o comprimento do primeiro iterável? Ou seja, e se eu passar range(1, 3)
ou range(1, 9999)
como o segundo iterável na função acima". E a resposta é simples: nada! Ok, isso não é verdade. "Nada" acontece no sentido de que a função map()
não levantará nenhuma exceção, ela simplesmente vai iterar sobre os elementos até que não encontre mais um segundo argumento para a função, ponto em que ela simplesmente para e retorna o resultado.
Assim, por exemplo, se você avaliar result = list(map(round, circle_areas, range(1, 3)))
, você não receberá nenhum erro, mesmo que o comprimento de circle_areas
e o comprimento de range(1, 3)
sejam diferentes. Em vez disso, é isso que o Python faz: ele usa o primeiro elemento de circle_areas
e o primeiro elemento de range(1,3)
e passa para round
. round
o avalia e depois salva o resultado. Depois, ele avança para a segunda iteração, segundo elemento de circle_areas
e segundo elemento de range(1,3)
, round
salva novamente. Agora, na terceira iteração (circle_areas
tem um terceiro elemento), o Python usa o terceiro elemento de circle_areas
e então tenta usar o terceiro elemento de range(1,3)
, mas como range(1,3)
não tem um terceiro elemento, o Python simplesmente para e retorna o resultado, que neste caso seria simplesmente [3.6, 5.58]
.
Vá em frente, experimente.
# Python 3
circle_areas = [3.56773, 5.57668, 4.00914, 56.24241, 9.01344, 32.00013]
result = list(map(round, circle_areas, range(1, 3)))
print(result)
A mesma coisa acontece se circle_areas
for menor que o comprimento do segundo iterável. O Python simplesmente para quando não consegue encontrar o próximo elemento em um dos iteráveis.
Para consolidar nosso conhecimento da função map()
, vamos usá-la para implementar nossa própria função zip()
. A função zip()
é uma função que pega um número de iteráveis e então cria uma tupla contendo cada um dos elementos nos iteráveis. Como map()
, no Python 3, retorna um objeto gerador, que pode ser facilmente convertido para uma lista chamando a função embutida list
sobre ele. Use a sessão do interpretador abaixo para entender o zip()
antes de criarmos a nossa com map()
.
# Python 3
my_strings = ['a', 'b', 'c', 'd', 'e']
my_numbers = [1, 2, 3, 4, 5]
results = list(zip(my_strings, my_numbers))
print(results)
Como um bônus, você consegue adivinhar o que aconteceria na sessão acima se my_strings
e my_numbers
não tiverem o mesmo comprimento? Não? Experimente! Mude o comprimento de um deles.
Vamos para nossa própria função zip()
!
# Python 3
my_strings = ['a', 'b', 'c', 'd', 'e']
my_numbers = [1, 2, 3, 4, 5]
results = list(map(lambda x, y: (x, y), my_strings, my_numbers))
print(results)
Olha só isso! Temos o mesmo resultado que zip
.
Você também reparou que eu nem precisei criar uma função usando o modo padrão def my_function()
? É assim que o map()
é flexível, e o Python em geral também é! Eu simplesmente usei uma função lambda
. Isso não quer dizer que usar o método de definição padrão de função (de def function_name()
) não seja permitido, ainda é. Eu simplesmente preferi escrever menos código (ser "Pythônico").
Isso é tudo sobre o map. Vamos ao filter()
.
Filter
Enquanto map()
passa cada elemento no iterável através de uma função e retorna o resultado de todos os elementos tendo passado pela função, filter()
, antes de tudo, exige que a função retorne valores booleanos (verdadeiro ou falso) e então passa cada elemento no iterável através da função, "filtrando" aqueles que são falsos. Ele tem a seguinte sintaxe:
filter(func, iterable)
Os seguintes pontos devem ser observados em relação ao filter()
:
- Ao contrário do
map()
, apenas um iterável é necessário. - O argumento
func
precisa retornar um tipo booleano. Se não o fizer,filter
simplesmente retorna oiterable
passado para ele. Além disso, como apenas um iterável é necessário, é implícito quefunc
deve aceitar apenas um argumento. filter
passa cada elemento no iterável através defunc
e retorna somente os que resultam em verdadeiro. Quero dizer, está bem ali no nome -- um "filtro".
Vamos ver alguns exemplos
O seguinte é uma lista (iterable
) das notas de 10 alunos em um exame de Química. Vamos filtrar aqueles que passaram com notas superiores a 75... usando filter
.
# Python 3
scores = [66, 90, 68, 59, 76, 60, 88, 74, 81, 65]
def is_A_student(score):
return score > 75
over_75 = list(filter(is_A_student, scores))
print(over_75)
O próximo exemplo será um detector de palíndromos. Um "palíndromo" é uma palavra, frase ou sequência que se lê da mesma forma de trás para frente. Vamos filtrar palavras que são palíndromos a partir de uma tupla (iterable
) de suspeitas de palíndromos.
# Python 3
dromes = ("demigod", "rewire", "madam", "freer", "anutforajaroftuna", "kiosk")
palindromes = list(filter(lambda word: word == word[::-1], dromes))
print(palindromes)
Que deve resultar em ['madam', 'anutforajaroftuna']
.
Bastante legal, né? Finalmente, reduce()
Reduce
reduce
aplica uma função de dois argumentos cumulativamente aos elementos de um iterável, opcionalmente começando com um argumento inicial. Ele tem a seguinte sintaxe:
reduce(func, iterable[, initial])
Onde func
é a função à qual cada elemento no iterable
é aplicado cumulativamente, e initial
é o valor opcional que é colocado antes dos elementos do iterável no cálculo, e serve como um padrão quando o iterável está vazio. O seguinte deve ser observado sobre reduce()
:
1. func
requer dois argumentos, sendo o primeiro o primeiro elemento no iterable
(se initial
não for fornecido) e o segundo elemento no iterable
. Se initial
for fornecido, então ele se torna o primeiro argumento para func
e o primeiro elemento no iterable
se torna o segundo elemento.
2. reduce
"reduz" (eu sei, me perdoe) iterable
em um único valor.
Como de costume, vamos ver alguns exemplos.
Vamos criar nossa própria versão da função embutida sum()
do Python. A função sum()
retorna a soma de todos os itens no iterável passado para ela.
# Python 3
from functools import reduce
numbers = [3, 4, 6, 9, 34, 12]
def custom_sum(first, second):
return first + second
result = reduce(custom_sum, numbers)
print(result)
O resultado, como você espera, é 68
.
Então, o que aconteceu?
Como de costume, é tudo sobre iterações: reduce
pega o primeiro e o segundo elementos em numbers
e os passa para custom_sum
respectivamente. custom_sum
calcula a soma deles e retorna isso para reduce
. reduce
então pega esse resultado e o aplica como o primeiro elemento para custom_sum
e pega o próximo elemento (terceiro) em numbers
como o segundo elemento para custom_sum
. Ele faz isso continuamente (cumulativamente) até que numbers
seja esgotado.
Vamos ver o que acontece quando eu uso o valor opcional initial
.
# Python 3
from functools import reduce
numbers = [3, 4, 6, 9, 34, 12]
def custom_sum(first, second):
return first + second
result = reduce(custom_sum, numbers, 10)
print(result)
O resultado, como você espera, é 78
porque reduce
, inicialmente, usa 10
como o primeiro argumento para custom_sum
.
Isso é tudo sobre Map, Reduce e Filter do Python. Tente os exercícios abaixo para ajudar a confirmar o seu entendimento de cada função.
Exercício
Neste exercício, você usará cada uma das funções map
, filter
, e reduce
para corrigir o código quebrado.
from functools import reduce
# Use map to print the square of each numbers rounded
# to three decimal places
my_floats = [4.35, 6.09, 3.25, 9.77, 2.16, 8.88, 4.59]
# Use filter to print only the names that are less than
# or equal to seven letters
my_names = ["olumide", "akinremi", "josiah", "temidayo", "omoseun"]
# Use reduce to print the product of these numbers
my_numbers = [4, 6, 9, 23, 5]
# Fix all three respectively.
map_result = list(map(lambda x: x, my_floats))
filter_result = list(filter(lambda name: name, my_names, my_names))
reduce_result = reduce(lambda num1, num2: num1 * num2, my_numbers, 0)
print(map_result)
print(filter_result)
print(reduce_result)
#### Map
from functools import reduce
my_floats = [4.35, 6.09, 3.25, 9.77, 2.16, 8.88, 4.59]
my_names = ["olumide", "akinremi", "josiah", "temidayo", "omoseun"]
my_numbers = [4, 6, 9, 23, 5]
map_result = list(map(lambda x: round(x ** 2, 3), my_floats))
filter_result = list(filter(lambda name: len(name) <= 7, my_names))
reduce_result = reduce(lambda num1, num2: num1 * num2, my_numbers)
print(map_result)
print(filter_result)
print(reduce_result)
test_output_contains("[18.922, 37.088, 10.562, 95.453, 4.666, 78.854, 21.068]")
test_output_contains("['olumide', 'josiah', 'omoseun']")
test_output_contains("24840")
success_msg("Congrats! Nice work.")
This site is generously supported by DataCamp. DataCamp offers online interactive Python Tutorials for Data Science. Join over a million other learners and get started learning Python for data science today!