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 έχει ακριβώς αυτόν τον αριθμό ως απαιτούμενα επιχειρήματα εισόδου. Πριν προχωρήσουμε σε ένα παράδειγμα, είναι σημαντικό να σημειώσετε τα εξής:

  1. Στην Python 2, η συνάρτηση map() επιστρέφει μία λίστα. Στην Python 3, ωστόσο, η συνάρτηση επιστρέφει ένα αντικείμενο map που είναι ένα αντικείμενο γεννήτριας. Για να λάβετε το αποτέλεσμα ως λίστα, μπορείτε να καλέσετε τη συνάρτηση list() στο αντικείμενο map, δηλαδή: list(map(func, *iterables)).
  2. Ο αριθμός των επιχειρημάτων προς τη 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():

  1. Σε αντίθεση με τη map(), απαιτείται μόνο ένας επαναλήπτης.
  2. Το επιχείρημα func απαιτείται να επιστρέψει έναν λογικό τύπο. Αν δεν το κάνει, η filter απλά επιστρέφει τον επαναλήπτη που της έχει δοθεί. Επίσης, δεδομένου ότι απαιτείται μόνο ένας επαναλήπτης, είναι σαφές ότι η συνάρτηση func πρέπει να δέχεται μόνο ένα επιχείρημα.
  3. Η 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!

Previous Tutorial Next Tutorial Take the Test
Copyright © learnpython.org. Read our Terms of Use and Privacy Policy