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
са вградени в Python (в модула __builtins__
) и не изискват импортиране. reduce
, обаче, трябва да бъде импортиран, тъй като се намира в модула functools
. Нека получим по-добро разбиране за това как работят те, като започнем с map
.
Map
Функцията map()
в Python има следния синтаксис:
map(func, *iterables)
Където func
е функцията, която ще бъде приложена върху всеки елемент от iterables
(толкова, колкото са те). Забелязахте ли звездичката (*
) върху iterables
? Тя означава, че може да има толкова много итерируеми обекти, колкото е възможно, докато func
има точно този брой, който е необходим като входни аргументи. Преди да преминем към пример, важно е да отбележите следното:
- В Python 2, функцията
map()
връща списък. В Python 3, обаче, функцията връщаmap обект
, който е генераторен обект. За да получите резултат като списък, може да се извика вградената функцияlist()
върху map обекта. т.е.list(map(func, *iterables))
- Броят на аргументите към
func
трябва да съответства на броя на изброенитеiterables
.
Нека видим как тези правила се прилагат със следните примери.
Нека имам списък (iterable
) от любимите си имена на домашни любимци, всички в малки букви, и трябва да ги преобразувам в главни букви. Традиционно, при нормално програмиране на Python, бих направил нещо такова:
my_pets = ['alfred', 'tabitha', 'william', 'arla']
uppered_pets = []
for pet in my_pets:
pet_ = pet.upper()
uppered_pets.append(pet_)
print(uppered_pets)
Което ще изведе ['ALFRED', 'TABITHA', 'WILLIAM', 'ARLA']
С функциите map()
, това не само е по-лесно, но и много по-гъвкаво. Просто правя следното:
# Python 3
my_pets = ['alfred', 'tabitha', 'william', 'arla']
uppered_pets = list(map(str.upper, my_pets))
print(uppered_pets)
Което също ще изведе същия резултат. Обърнете внимание, че използвахме дефинирания по-горе синтаксис на map()
, в този случай func
е str.upper
, а iterables
е списъкът my_pets
-- само един итерируем обект. Също така, обърнете внимание, че не сме извикали функцията str.upper
(правейки това: str.upper()
), тъй като функцията map прави това сама за всеки елемент в списъка my_pets
.
Още по-важно е да се отбележи, че функцията str.upper
изисква само един аргумент по дефиниция и затова подадохме само един итерируем обект. Така че, ако функцията, която подавате, изисква два, три или n аргумента, то трябва да ѝ предадете съответно два, три или n итерируеми обекта. Нека поясня това с още един пример.
Нека имам списък от площи на кръгове, които съм изчислил някъде, всички с точност до пет знака след десетичната запетая. И трябва да закръгля всеки елемент в списъка до десетичните позиции на неговото положение, което означава, че трябва да закръгля първия елемент в списъка до един знак след десетичната запетая, втория елемент до два знака след десетичната запетая и т.н. С map()
това е лесна задача. Нека видим как.
Python вече ни е снабдил с вградената функция round()
, която приема два аргумента -- число, което да се закръгли, и брой знаци след десетичната запетая, до които да се закръгли числото. Така че, тъй като функцията изисква два аргумента, трябва да предадем два итерируеми обекта.
# 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)
Виждате ли красотата на map()
? Можете ли да си представите колко гъвкавост това предлага?
Функцията range(1, 7)
действа като втори аргумент на функцията round
(броят на необходимите знаци след десетичната запетая на итерация). Така че докато map
итерации през circle_areas
, по време на първата итерация първият елемент на circle_areas
, 3.56773
е подаден заедно с първия елемент на range(1,7)
, 1
към round
, като го прави ефективно round(3.56773, 1)
. По време на втора итерация, вторият елемент на circle_areas
, 5.57668
, заедно с втория елемент на range(1,7)
, 2
е подаден към round
, като го превръща в round(5.57668, 2)
. Това се случва, докато списъкът circle_areas
не бъде изчерпан.
Сигурно се чудите: "Какво ще стане, ако подам итерируем обект по-малък или по-голям от дължината на първия итерируем обект? Тоест, какво ако подам range(1, 3)
или range(1, 9999)
като втори итерируем обект в горната функция". И отговорът е прост: нищо! Добре, това не е вярно. "Нищо" означава, че функцията map()
няма да хвърли изключение, тя просто ще итерира върху елементите, докато не намери втори аргумент за функцията, при което ще спре и ще върне резултата.
reduce
прилага функция с два аргумента кумулативно върху елементите на итерируем обект, евентуално започвайки с началния аргумент. Тя има следния синтаксис:
reduce(func, iterable[, initial])
Където func
е функцията, върху която всеки елемент в iterable
се прилага кумулативно, а initial
е помощното значение, което се поставя преди елементите на итерируемия обект в изчислението и служи като стандартна стойност, когато итерируемият обект е празен. Следното трябва да се отбележи за reduce()
:
1. func
изисква два аргумента, от които първият е първият елемент в iterable
(ако initial
не е предоставено) и вторият елемент в iterable
. Ако initial
е предоставено, то тя става първият аргумент за func
, а първият елемент в iterable
става втори елемент.
2. reduce
"намалява" (знам, простете ми) iterable
в едно единствено значение.
Както обикновено, нека видим някои примери.
Нека създадем наша собствена версия на вградената функция sum()
на Python. Функцията sum()
връща сумата на всички елементи в предадения итерируем обект.
# 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)
Резултатът, както очаквате, е 68
.
И така, какво се случи?
Както обикновено, всичко е свързано с итерации: reduce
взема първия и втория елемент в numbers
и ги предава на custom_sum
. custom_sum
изчислява тяхната сума и я връща на reduce
. reduce
след това взема този резултат и го прилага като първи елемент на custom_sum
и взема следващия елемент (трети) в numbers
като втори елемент на custom_sum
. Това се прави непрекъснато (кумулативно), докато numbers
не бъде изчерпан.
Нека видим какво се случва, когато използвам опционалната стойност 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)
Резултатът, както ще очаквате, е 78
, защото reduce
първоначално използва 10
като първи аргумент към custom_sum
.
Това е всичко за Map, Reduce и Filter в Python. Опитайте следните упражнения, за да установите разбирането си за всяка функция.
Exercise
В това упражнение ще използвате всяка от функциите map
, filter
и reduce
за да поправите счупен код.
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!