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
Map, Filter, Reduce
Map, Filter och Reduce är paradigmer inom funktionell programmering. De tillåter programmeraren (du) att skriva enklare, kortare kod utan att nödvändigtvis behöva bry sig om detaljer som loopar och greningar.
I huvudsak tillåter dessa tre funktioner dig att tillämpa en funktion på ett antal iterables, i ett enda svep. map
och filter
är inbyggda i Python (i __builtins__
modulen) och kräver ingen importering. reduce
däremot måste importeras eftersom den finns i functools
modulen. Låt oss få en bättre förståelse för hur de fungerar, med start på map
.
Map
Funktionen map()
i python har följande syntax:
map(func, *iterables)
Där func
är funktionen som skall appliceras på varje element i iterables
(så många som de är). Ser du asterisken (*
) på iterables
? Det betyder att det kan finnas så många iterables som möjligt, så länge func
har det exakta antalet som krävs som inmatningsargument. Innan vi går vidare till ett exempel är det viktigt att du noterar följande:
- I Python 2 returnerar
map()
funktionen en lista. I Python 3, däremot, återger funktionen ettmap object
som är ett generatorobjekt. För att få resultatet som en lista kan den inbyggda funktionenlist()
anropas på map-objektet, dvs.list(map(func, *iterables))
- Antalet argument för
func
måste vara lika med antaletiterables
som anges.
Låt oss se hur dessa regler fungerar med följande exempel.
Anta att jag har en lista (iterable
) av mina favoritdjur, alla i gemener och jag behöver dem i versaler. Traditionellt, i vanlig python-användning, skulle jag göra något sånt här:
my_pets = ['alfred', 'tabitha', 'william', 'arla']
uppered_pets = []
for pet in my_pets:
pet_ = pet.upper()
uppered_pets.append(pet_)
print(uppered_pets)
Vilket skulle ge ut ['ALFRED', 'TABITHA', 'WILLIAM', 'ARLA']
Med map()
funktioner är det inte bara enklare, utan det är också mycket mer flexibelt. Jag gör helt enkelt detta:
# Python 3
my_pets = ['alfred', 'tabitha', 'william', 'arla']
uppered_pets = list(map(str.upper, my_pets))
print(uppered_pets)
Vilket också skulle ge samma resultat. Notera att med den definierade map()
syntaxen ovan, är func
i detta fallet str.upper
och iterables
är listan my_pets
, endast en iterable. Notera också att vi inte anropar funktionen str.upper
(gör detta: str.upper()
), eftersom map-funktionen gör det åt oss på varje element i listan my_pets
.
Vad som är viktigt att notera är att funktionen str.upper
kräver endast ett argument enligt definition och därför passade vi bara in en iterable till det. Så, om funktionen du skickar kräver två, eller tre, eller n argument, då behöver du också skicka in två, tre eller n iterables till den. Låt mig klargöra detta med ett annat exempel.
Anta att jag har en lista över cirkelområden som jag beräknade någonstans, alla med fem decimalers noggrannhet. Och jag behöver avrunda varje element i listan till sina egna positions decimalplatser, vilket innebär att jag måste avrunda det första elementet i listan till en decimal, det andra elementet i listan till två decimaler, det tredje elementet i listan till tre decimaler, etc. Med map()
är detta en enkel match. Låt oss se hur.
Python välsignar oss redan med den inbyggda funktionen round()
som tar två argument -- talet som ska avrundas och antalet decimalplatser som talet ska avrundas upp till. Så, eftersom funktionen kräver två argument, behöver vi skicka in två iterables.
# 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)
Ser du skönheten i map()
? Kan du föreställa dig vilken flexibilitet detta skapar?
Funktionen range(1, 7)
fungerar som det andra argumentet till funktionen round
(antalet önskade decimalplatser per iteration). Så när map
itererar igenom circle_areas
, under den första iterationen överlämnas det första elementet i circle_areas
, 3.56773
tillsammans med det första elementet i range(1,7)
, 1
till round
vilket gör det effektivt till round(3.56773, 1)
. Vid den andra iterationen överlämnas det andra elementet i circle_areas
, 5.57668
tillsammans med det andra elementet i range(1,7)
, 2
till round
vilket gör det till round(5.57668, 2)
. Detta sker till slutet av listan circle_areas
nås.
Jag antar att du undrar: "Vad händer om jag skickar in en iterable som är kortare eller längre än längden på den första iterablen? Det vill säga, vad händer om jag skickar in range(1, 3)
eller range(1, 9999)
som den andra iterablen i ovanstående funktion". Och svaret är enkelt: ingenting! Okej, det är inte sant. "Ingenting" händer i den meningen att funktionen map()
inte kommer att ge något undantag, det kommer helt enkelt att iterera över elementen tills det inte kan hitta ett andra argument till funktionen, vid vilket tillfälle det helt enkelt stannar och returnerar resultatet.
Så, till exempel, om du utvärderar result = list(map(round, circle_areas, range(1, 3)))
, kommer du inte att få något fel även om längden på circle_areas
och längden på range(1, 3)
skiljer sig åt. Istället gör Python så här: Den tar det första elementet i circle_areas
och det första elementet i range(1,3)
och skickar det till round
. round
utvärderar det och sparar resultatet. Sedan går den vidare till den andra iterationen, med det andra elementet i circle_areas
och det andra elementet i range(1,3)
, sparar round
det igen. Nu, i den tredje iterationen (circle_areas
har ett tredje element), tar Python det tredje elementet i circle_areas
och försöker sedan ta det tredje elementet av range(1,3)
men eftersom range(1,3)
inte har ett tredje element, stannar Python helt enkelt och returnerar resultatet, vilket i detta fall skulle vara [3.6, 5.58]
.
Gå vidare, prova det.
# 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)
Samma sak händer om circle_areas
är kortare än längden på den andra iterablen. Python slutar helt enkelt när den inte kan hitta nästa element i en av iterablarna.
För att bekräfta vår kunskap om funktionen map()
ska vi använda den för att implementera vår egen anpassade zip()
funktion. Funktionen zip()
är en funktion som tar ett antal iterables och sedan skapar en tuple som innehåller varje element i iterablarna. Liksom map()
, returnerar den i Python 3 ett generatorobjekt, som enkelt kan konverteras till en lista genom att anropa den inbyggda funktionen list
på den. Använd tolkexemplet nedan för att få grepp om zip()
innan vi skapar vår egen med 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)
Som en bonus kan du gissa vad som skulle hända i sessionen ovan om my_strings
och my_numbers
inte har samma längd? Nej? prova det! Ändra längden på en av dem.
Till vår egen anpassade zip()
funktion!
# 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)
Titta på det! Vi har samma resultat som zip
.
Har du också märkt att jag inte ens behövde skapa en funktion med hjälp av def my_function()
standardmetoden? Det är hur flexibel map()
, och Python i allmänhet, är! Jag använde helt enkelt en lambda
funktion. Detta betyder inte att användningen av standardmetoden för att definiera funktioner (av def function_name()
) inte är tillåten, det är det fortfarande. Jag föredrog helt enkelt att skriva mindre kod (vara "Pythonic").
Det var allt om map. Till filter()
Filter
Medan map()
skickar varje element i iterablen genom en funktion och returnerar resultatet av alla element som har passerat genom funktionen, kräver filter()
först och främst att funktionen returnerar booleska värden (true eller false) och skickar sedan varje element i iterablen genom funktionen, "filtrerar bort" de som är falska. Det har följande syntax:
filter(func, iterable)
Följande punkter bör noteras beträffande filter()
:
- Till skillnad från
map()
krävs endast en iterable. - Argumentet
func
är kravet på att returnera en boolesk typ. Om det inte gör det, returnerarfilter
helt enkelt detiterable
som skickas till det. Eftersom endast en iterable krävs är det underförstått attfunc
bara kan ta ett argument. filter
skickar varje element i iterablen genomfunc
och returnerar endast de som utvärderas till sanna. Jag menar, det står där i namnet -- ett "filter".
Låt oss se några exempel
Följande är en lista (iterable
) över resultaten från 10 elever i ett kemi-test. Låt oss filtrera ut de som klarade med resultat över 75...using 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)
Nästa exempel blir en palindromdetektor. En "palindrom" är ett ord, en fras eller en sekvens som läses samma bakåt som framåt. Låt oss filtrera ut ord som är palindromer från en tuple (iterable
) av misstänkta palindromer.
# Python 3
dromes = ("demigod", "rewire", "madam", "freer", "anutforajaroftuna", "kiosk")
palindromes = list(filter(lambda word: word == word[::-1], dromes))
print(palindromes)
Vilket bör ge ut ['madam', 'anutforajaroftuna']
.
Ganska snyggt va? Slutligen, reduce()
Reduce
reduce
tillämpar en funktion av två argument kumulativt på elementen i en iterable, eventuellt med start med ett initialt argument. Den har följande syntax:
reduce(func, iterable[, initial])
Där func
är funktionen på vilken varje element i iterable
får tillämpas kumulativt, och initial
är det valfria värdet som placeras före elementen i iterablen i beräkningen, och fungerar som standard när iterablen är tom. Följande bör noteras om reduce()
:
1. func
kräver två argument, den första av dem är det första elementet i iterable
(om initial
inte anges) och det andra elementet i iterable
. Om initial
anges, så blir den första argumentet till func
och det första elementet i iterable
blir det andra elementet.
2. reduce
"reducerar" (jag vet, förlåt mig) iterable
till ett enda värde.
Som vanligt, låt oss se några exempel.
Låt oss skapa vår egen version av Pythons inbyggda funktion sum()
. Funktionen sum()
returnerar summan av alla element i iterablen som skickas till den.
# 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)
Resultatet, som du förväntar dig, är 68
.
Så, vad hände?
Som vanligt handlar det om iterationer: reduce
tar det första och andra elementet i numbers
och skickar dem till custom_sum
respektive. custom_sum
beräknar deras summa och returnerar det till reduce
. reduce
tar sedan det resultatet och tillämpar det som det första argumentet i custom_sum
och tar nästa element (det tredje) i numbers
som det andra elementet i custom_sum
. Det gör detta kontinuerligt (kumulativt) tills numbers
är uttömda.
Låt oss se vad som händer när jag använder det valfria initial
värdet.
# 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)
Resultatet, som du förväntar dig, är 78
eftersom reduce
, initialt, använder 10
som det första argumentet till custom_sum
.
Det var allt om Pythons Map, Reduce och Filter. Prova på nedanstående övningar för att hjälpa dig förstå varje funktion.
Övning
I denna övning kommer du att använda var och en av map
, filter
och reduce
för att fixa trasig kod.
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!