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 (στο module __builtins__
) και δεν απαιτούν εισαγωγή. Η reduce
, ωστόσο, πρέπει να εισαχθεί, καθώς βρίσκεται στο module 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
πρέπει να είναι ο αριθμός των επαναληπτών που αναφέρονται.
Ας δούμε πώς λειτουργούν αυτοί οι κανόνες με τα ακόλουθα παραδείγματα.
Ας πούμε ότι έχω μια λίστα (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()
δεν θα ανεβάσει καμία εξαίρεση, απλά θα πραγματοποιήσει επανάληψη στα στοιχεία μέχρι να βρει μια δεύτερη ανυπαρξία επιχείρηματος στη συνάρτηση, στο σημείο στο οποίο απλά σταματά και επιστρέφει το αποτέλεσμα.
Έτσι, για παράδειγμα, αν εκτελέσετε την αξιολόγηση της result = list(map(round, circle_areas, range(1, 3)))
, δεν θα λάβετε καμία σφάλμα, ακόμα και αν το μήκος των circle_areas
και το μήκος της range(1, 3)
διαφέρουν. Αντ' αυτού, αυτό είναι που κάνει η Python: Παίρνει το πρώτο στοιχείο των circle_areas
και το πρώτο στοιχείο της range(1,3)
και το περνά στη round
. Η round
το αξιολογεί και στη συνέχεια αποθηκεύει το αποτέλεσμα. Στη συνέχεια, πηγαίνει στη δεύτερη επανάληψη, το δεύτερο στοιχείο των circle_areas
και το δεύτερο στοιχείο της range(1,3)
, η round
το αποθηκεύει ξανά. Τώρα, στην τρίτη επανάληψη (οι circle_areas
έχουν τρίτο στοιχείο), η Python παίρνει το τρίτο στοιχείο των circle_areas
και στη συνέχεια προσπαθεί να πάρει το τρίτο στοιχείο της range(1,3)
, αλλά αφού η range(1,3)
δεν έχει τρίτο στοιχείο, η Python απλά σταματά και επιστρέφει το αποτέλεσμα, το οποίο σε αυτήν την περίπτωση θα είναι απλά [3.6, 5.58]
.
Προχωρήστε, δοκιμάστε το.
# 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)
Το ίδιο πράγμα συμβαίνει εάν οι circle_areas
είναι λιγότεροι από το μήκος του δεύτερου επαναλήπτη. Η Python απλά σταματά όταν δεν μπορεί να βρει το επόμενο στοιχείο σε έναν από τους επαναλήπτες.
Για να ενισχύσουμε τις γνώσεις μας σχετικά με τη συνάρτηση map()
, πρόκειται να τη χρησιμοποιήσουμε για να υλοποιήσουμε τη δική μας custom συναρτήση zip()
. Η συνάρτηση zip()
είναι μια συνάρτηση που παίρνει έναν αριθμό επαναληπτών και στη συνέχεια δημιουργεί μια πλειάδα περιέχοντας το καθένα από τα στοιχεία στις επαναλήψεις. Όπως η map()
, στην Python 3, επιστρέφει ένα αντικείμενο γεννήτριας, που μπορεί να μετατραπεί εύκολα σε λίστα καλώντας τη συνάρτηση list
πάνω σε αυτό. Χρησιμοποιήστε την παρακάτω συνεδρία ερμηνευτή για να κατανοήσετε τη zip()
πριν δημιουργήσουμε τη δική μας με τη 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)
Ως μπόνους, μπορείτε να μαντέψετε τι θα συμβεί στην παραπάνω συνεδρία εάν οι my_strings
και οι my_numbers
δεν έχουν το ίδιο μήκος; Όχι; δοκιμάστε το! Αλλάξτε το μήκος ενός από αυτά.
Προχωρώντας στη δική μας custom συνάρτηση 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)
Απλά κοιτάξτε αυτό! Έχουμε το ίδιο αποτέλεσμα όπως το zip
.
Παρατηρήσατε επίσης ότι δεν χρειάστηκε καν να δημιουργήσω μια συνάρτηση χρησιμοποιώντας τον κανονικό τρόπο def my_function()
; Αυτή είναι η ευελιξία της map()
, και της Python γενικότερα! Απλά χρησιμοποίησα μια λειτουργία lambda
. Αυτό δεν σημαίνει ότι η χρήση της τυπικής μεθόδου ορισμού συνάρτησης (με το def function_name()
) δεν επιτρέπεται, ακόμα είναι. Απλά προτίμησα να γράψω λιγότερο κώδικα (ακολουθώντας τον "Pythonic" τρόπο).
Αυτό είναι όλο για τη map. Παράλληλα στην filter()
Filter
Ενώ η map()
περνά κάθε στοιχείο στον επαναλήπτη μέσα από μια συνάρτηση και επιστρέφει το αποτέλεσμα όλων των στοιχείων που πέρασαν από τη συνάρτηση, η filter()
, πρώτα απ' όλα, απαιτεί από τη συνάρτηση να επιστρέφει λογικές τιμές (αληθές ή ψευδές) και στη συνέχεια περνά κάθε στοιχείο στον επαναλήπτη μέσα από τη συνάρτηση, "φιλτράροντας" αυτά που είναι ψευδή. Έχει την ακόλουθη σύνταξη:
filter(func, iterable)
Οι ακόλουθες σημεία πρέπει να σημειωθούν όσον αφορά τη filter()
:
- Σε αντίθεση με τη
map()
, απαιτείται μόνο ένας επαναλήπτης. - Το επιχείρημα
func
απαιτείται να επιστρέψει έναν λογικό τύπο. Αν δεν το κάνει, ηfilter
απλά επιστρέφει τον επαναλήπτη που της έχει δοθεί. Επίσης, δεδομένου ότι απαιτείται μόνο ένας επαναλήπτης, είναι σαφές ότι η συνάρτησηfunc
πρέπει να δέχεται μόνο ένα επιχείρημα. - Η
filter
περνά κάθε στοιχείο στον επαναλήπτη μέσω τηςfunc
και επιστρέφει μόνο αυτά που αξιολογούνται ως αληθή. Δηλαδή, είναι σωστό να λέμε ότι είναι "φίλτρο".
Ας δούμε μερικά παραδείγματα
Η παρακάτω είναι μια λίστα (iterable
) με τις βαθμολογίες 10 μαθητών σε μια εξέταση Χημείας. Ας φιλτράρουμε αυτούς που πέρασαν με βαθμολογία πάνω από 75... χρησιμοποιώντας τη 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)
Στο επόμενο παράδειγμα θα δημιουργήσουμε έναν ανιχνευτή παλίνδρομου. Ένας "παλίνδρομος" είναι μια λέξη, φράση ή ακολουθία που διαβάζεται το ίδιο και προς τα εμπρός και προς τα πίσω. Ας φιλτράρουμε λέξεις που είναι παλίνδρομοι από μια πλειάδα (iterable
) υποτιθέμενων παλίνδρομων.
# Python 3
dromes = ("demigod", "rewire", "madam", "freer", "anutforajaroftuna", "kiosk")
palindromes = list(filter(lambda word: word == word[::-1], dromes))
print(palindromes)
Το οποίο θα εξάγει ['madam', 'anutforajaroftuna']
.
Αρκετά κομψό, έτσι δεν είναι; Τέλος, η reduce()
Reduce
Η reduce
εφαρμόζει μια συνάρτηση δύο επιχειρημάτων συσσωρευτικά στα στοιχεία ενός επαναλήπτη, ενδεχομένως ξεκινώντας με ένα αρχικό επιχείρημα. Έχει την ακόλουθη σύνταξη:
reduce(func, iterable[, initial])
Όπου η func
είναι η συνάρτηση στην οποία κάθε στοιχείο στον επαναλήπτη εφαρμόζεται συσσωρευτικά, και η initial
είναι η προαιρετική τιμή που τοποθετείται πριν από τα στοιχεία του επαναλήπτη στον υπολογισμό, και λειτουργεί ως προεπιλεγμένη όταν ο επαναλήπτης είναι κενός. Τα παρακάτω πρέπει να σημειώνονται σχετικά με τη reduce()
:
1. Η func
απαιτεί δύο επιχειρήματα, στο πρώτο από τα οποία είναι το πρώτο στοιχείο στον επαναλήπτη (αν δεν παρέχεται το initial
) και το δεύτερο στοιχείο στον επαναλήπτη. Αν παρέχεται το initial
, τότε αυτό γίνεται το πρώτο επιχείρημα στη func
και το πρώτο στοιχείο στον επαναλήπτη γίνεται το δεύτερο στοιχείο.
2. Η reduce
"μειώνει" (ξέρω, συγχωρέστε με) τον επαναλήπτη σε μια μοναδική τιμή.
Όπως πάντα, ας δούμε μερικά παραδείγματα.
Ας δημιουργήσουμε τη δική μας εκδοχή της ενσωματωμένης συνάρτησης 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. Δοκιμάστε τις παρακάτω ασκήσεις για να επιβεβαιώσετε την κατανόησή σας κάθε συνάρτησης.
Άσκηση
Σε αυτή την άσκηση, θα χρησιμοποιήσετε καθεμία από τις 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!