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
Térkép, Szűrés, Redukálás
Map, Filter és Reduce a funkcionális programozás paradigmái. Lehetővé teszik a programozó számára, hogy egyszerűbb, rövidebb kódot írjon anélkül, hogy szükségszerűen foglalkoznia kellene az olyan bonyolultságokkal, mint a ciklusok és az elágazások.
Alapvetően ez a három funkció lehetővé teszi, hogy egy függvényt alkalmazzunk több iterálandó elemre egyetlen lépésben. A map
és a filter
beépített Python funkciók (a __builtins__
modulban találhatók) és nem igényelnek importálást. A reduce
azonban importálást igényel, mivel a functools
modulban található. Lássuk meg jobban, hogyan működnek, kezdve a map
függvénnyel.
Map
A map()
függvény szintaxisa Pythonban a következő:
map(func, *iterables)
Ahol a func
az a függvény, amelyet az egyes elemekre az iterables
(amennyi csak van) fog alkalmazni. Észrevetted a csillagot (*
) az iterables
előtt? Ez azt jelenti, hogy annyi iterálható elem lehet, amennyi csak lehetséges, amennyiben a func
ennyi bemeneti argumentumot igényel. Mielőtt példákra térnénk, fontos megjegyezni a következőket:
- A Python 2-ben a
map()
függvény egy listát ad vissza. A Python 3-ban viszont a függvény egymap object
-et ad vissza, amely egy generátor objektum. Ahhoz, hogy az eredményt listaként kapjuk meg, a beépítettlist()
függvényt lehet meghívni a map objektumon. Azaz:list(map(func, *iterables))
- A
func
argumentumok száma meg kell, hogy egyezzen aziterables
felsorolt elemeinek számával.
Lássuk, hogyan működnek ezek a szabályok a következő példákkal.
Tegyük fel, hogy van egy listám (iterable
) a kedvenc háziállataim neveivel, mind kisbetűs, és nagybetűs formában szeretném őket. Hagyományosan, normál Python használatával valami ilyesmit tennék:
my_pets = ['alfred', 'tabitha', 'william', 'arla']
uppered_pets = []
for pet in my_pets:
pet_ = pet.upper()
uppered_pets.append(pet_)
print(uppered_pets)
Amely kimeneteként így jelenne meg: ['ALFRED', 'TABITHA', 'WILLIAM', 'ARLA']
A map()
függvényekkel nemcsak egyszerűbb, hanem sokkal rugalmasabb is. Egyszerűen ezt tenném:
# Python 3
my_pets = ['alfred', 'tabitha', 'william', 'arla']
uppered_pets = list(map(str.upper, my_pets))
print(uppered_pets)
Amely ugyanazt az eredményt adná. Megjegyzendő, hogy a meghatározott map()
szintaxis használatával ebben az esetben a func
az str.upper
, és az iterables
a my_pets
lista -- csupán egy iterálható elem. Fontos, hogy ne hívtuk meg az str.upper
függvényt (így: str.upper()
), mivel a map függvény ezt megteszi nekünk a my_pets
lista egyik elemén sem.
Ami még fontosabb megjegyezni, hogy az str.upper
függvény definíció szerint csak egy argumentumot igényel, így csak egy iterálható elemet adtunk át neki. Tehát, ha a függvény, amit átadsz, kettő, három vagy n argumentumot igényel, akkor kettő, három vagy n iterálható elemet kell átadnod neki. Hadd tisztázzam ezt egy másik példával.
Tegyük fel, hogy van egy lista a kör területekkel, amiket valahol kiszámoltam, mind öt tizedesjegyre. És minden elemet a helyiérték tízedesjegyre kell kerekítenem, ami azt jelenti, hogy az első elemet egy tizedesjegyre, a másodikat két tizedesjegyre, a harmadikat három tizedesjegyre, stb. kell kerekítenem. A map()
függvénnyel ez gyerekjáték. Lássuk, hogyan.
Python már megáldott bennünket a round()
beépített függvénnyel, amely két argumentumot igényel: a kerekítendő számot és a kerekítendő szám tizedesjegyeinek számát. Tehát, mivel a függvény két argumentumot igényel, két iterálható elemet kell adnunk hozzá.
# 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)
Látod a map()
szépségét? El tudod képzelni, milyen rugalmasságot vált ki ez?
A range(1, 7)
függvény a második argumentumként járul hozzá a round
függvényhez (az egyes iterációkhoz szükséges tizedesjegyek száma szerint). Amint a map
iterál a circle_areas
elemein, az első iteráció során a circle_areas
első eleme, 3.56773
és a range(1,7)
első eleme, 1
is átadásra kerül a round
függvénynek, így azt hatékonyan round(3.56773, 1)
-re teszi. A második iteráció során a második elem a circle_areas
-ból, 5.57668
és a második elem a range(1,7)
-ből, 2
átadásra kerül a round
függvénynek, ezáltal a művelet round(5.57668, 2)
lesz. Ez az iteráció addig folytatódik, ameddig el nem ér az circle_areas
lista végére.
Biztosan azon gondolkodsz: „Mi történik, ha az egyik iterálható elemet kevesebbel vagy többel adom meg, mint a másik iterálhatót? Azaz mi történik, ha a range(1, 3)
-t vagy a range(1, 9999)
-t adom meg mint második iterálható elemet a fenti függvényben?”. A válasz egyszerű: semmi! Oké, ez nem igaz. A „semmi” azt jelenti, hogy a map()
függvény nem generál semmilyen hibát, egyszerűen iterál az elemeken, amíg nem talál második argumentumot a függvényhez, ekkor egyszerűen megáll és visszatér az eredménnyel.
Például, ha a result = list(map(round, circle_areas, range(1, 3)))
-t értékesíted, nem kapsz hibát, még akkor sem, ha a circle_areas
hossza és a range(1, 3)
hossza eltér. Ehelyett Python ezt teszi: Az első elemet a circle_areas
-ból és az első elemet a range(1,3)
-ból átadja a round
függvénynek. A round
kiértékeli, majd elmenti az eredményt. Ezután a második iteráció, a második elem a circle_areas
-ból és a második elem a range(1,3)
-ból szintén mentésre kerül. Most, a harmadik iterációban (a circle_areas
-nak van harmadik eleme), Python megpróbálja megkeresni a harmadik elemet a range(1,3)
-ból, de mivel a range(1,3)
-nak nincs harmadik eleme, Python egyszerűen megáll és visszatér az eredménnyel, amely ebben az esetben egyszerűen [3.6, 5.58]
lesz.
Próbáld ki!
# 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)
Ugyanez történik, ha a circle_areas
rövidebb, mint a második iterálható elemek hossza. Python egyszerűen megáll, ha nem találja a következő elemet valamelyik iterálható elemből.
A map()
függvényről szerzett tudásunk megerősítése érdekében saját zip()
függvényünket fogjuk megvalósítani. A zip()
függvény egy olyan függvény, amely számos iterálható elemet vesz fel, és egy tuple-t készít, amely az iterálható elemek minden elemét tartalmazza. Mint a map()
, Python 3-ban ez egy generátor objektumot ad vissza, amelyet könnyen listára lehet konvertálni úgy, hogy a beépített list
függvényt meghívjuk rá. Használjuk az alábbi interpreter munkamenetet a zip()
megértéséhez, mielőtt saját magunkét készítenénk a map()
-pal.
# 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)
Bónuszként, tudnád tippelni, mi történne a fenti munkamenetben, ha a my_strings
és a my_numbers
nem azonos hosszúságúak? Nem? Próbáld ki! Változtasd meg egyikük hosszát.
Menjünk a saját egyedi zip()
függvényünkre!
# 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)
Csak nézd meg! Ugyanazt az eredményt kaptuk, mint a zip
.
Azt is észrevetted, hogy nem is kellett létrehoznom egy függvényt a def my_function()
standard módszerrel? Ez a rugalmasság a map()
-ben, és általában a Pythonban! Egyszerűen lambda függvényt használtam. Ez nem azt jelenti, hogy a szokásos függvénydefiníciós módszer használata (azaz def function_name()
) nem megengedett, továbbra is az. Egyszerűen azért választottam kevesebb kód írását (legyek „pythonic”).
Ez minden a map függvényről. Most jöjjön a filter()
Filter
Míg a map()
minden elemet az iterálhatóból átfuttat egy függvényen, és visszaadja az összes elem eredményét, amely átment a függvényen, a filter()
, először is megköveteli a függvénytől, hogy logikai értékeket (true vagy false) adjon vissza, majd minden elemet az iterálhatóból átfuttat a függvényen, "kiszelektálva" azokat, amelyek hamisak. Ennek a szintaxisa a következő:
filter(func, iterable)
Fel kell jegyezni a következő pontokat a filter()
-rel kapcsolatban:
- A különbség a
map()
-hoz képest, hogy csak egy iterálható elem szükséges. - A
func
argumentum megköveteli, hogy logikai típust adjon vissza. Ha nem teszi, afilter
egyszerűen visszaadja az általa kapottiterable
-t. Emellett, mivel csak egy iterálható szükséges, implicit módon afunc
csak egy argumentumot vár el. - A
filter
az iterálható minden elemét átadja afunc
-nak, és csak azokat adja vissza, amelyek true értéket adnak vissza. Gondolj bele, ott van a nevükben - egy "filter".
Nézzünk néhány példát
A következő egy lista (iterable
) tíz diák pontszámairól egy kémia vizsgán. Szelektáljuk ki azokat, akik több mint 75 ponttal mentek át...a filter
-t használva.
# 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)
A következő példa egy palindróma detektor lesz. A "palindróma" egy szó, kifejezés vagy sorozat, ami visszafelé olvasva is ugyanaz. Szelektáljuk ki a palindrómákat egy feltételezett palindrómákból álló tuple-ból (iterable
).
# Python 3
dromes = ("demigod", "rewire", "madam", "freer", "anutforajaroftuna", "kiosk")
palindromes = list(filter(lambda word: word == word[::-1], dromes))
print(palindromes)
Amely azt kell hogy kiadja: ['madam', 'anutforajaroftuna']
.
Elég menő, ugye? Végül a reduce()
.
Reduce
A reduce
egy két argumentumos függvényt alkalmaz az iterálható elemekre kumulatívan, opcionálisan egy kezdő argumentummal indulva. Ennek a szintaxisa a következő:
reduce(func, iterable[, initial])
Ahol a func
az a függvény, amelyre az iterable
minden eleme kumulatív módon alkalmazásra kerül, és az initial
az az opcionális érték, amely az iterálható elemek elejére kerül a számítás során, és alapértelmezett értékként szolgál, ha az iterálható üres. A következőket kell megjegyezni a reduce()
-ről:
1. A func
két argumentumot igényel, amelyek közül az első az iterálható első eleme (ha az initial
nincs megadva), és az iterálható második eleme. Ha az initial
meg van adva, akkor ez lesz az első argumentum az func
-nak, és az iterálható első elem lesz a második elem.
2. A reduce
"csökkenti" (tudom, bocsáss meg) az iterable
elemeit egyetlen értékké.
Mint mindig, nézzük meg a példákat.
Hozzuk létre a saját verziónkat a Python beépített sum()
függvényéről. A sum()
függvény visszaadja az átadott iterálható elemek összes elemének összegét.
# 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)
Az eredmény, ahogyan elvárható, 68
.
Tehát mi történt?
Ahogy megszokhattuk, az iterációk a lényeg: A reduce
fogja az első és második elemet a numbers
listából, és átadja őket a custom_sum
-nak. A custom_sum
kiszámítja azok összegét, és visszaküldi a reduce
-nak. A reduce
ezután az eredményt első elemként alkalmazza a custom_sum
-hoz, és a következő elemet (harmadik) a numbers
-ból második elemként, amitől egy folyamat felgyorsul (kumulatív módon), amíg a numbers
-t ki nem merítik.
Lássuk, mi történik, ha opcionális initial
értéket használok.
# 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)
Az eredmény, ahogy elvárható, 78
, mivel a reduce
, eleinte, a 10
-et használja első argumentumként a custom_sum
-nak.
Ez minden a Python Map, Reduce, és Filter funkcióiról. Próbáld ki az alábbi gyakorlatokat, hogy biztosan megértsd mindegyik funkciót.
Exercise
E gyakorlatban a map
, filter
, és reduce
használatával kell javítanod hibás kódokat.
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!