Πώς - και γιατί - πρέπει να χρησιμοποιήσετε το Python Generators

Οι γεννήτριες ήταν ένα σημαντικό μέρος της Python από τότε που παρουσιάστηκαν με το PEP 255.

Οι λειτουργίες της γεννήτριας σας επιτρέπουν να δηλώσετε μια συνάρτηση που συμπεριφέρεται σαν επαναληπτική.

Επιτρέπουν στους προγραμματιστές να κάνουν μια επανάληψη με γρήγορο, εύκολο και καθαρό τρόπο.

Τι είναι μια επανάληψη, μπορείτε να ρωτήσετε;

Ο επαναληπτής είναι ένα αντικείμενο που μπορεί να επαναληφθεί (βρόχο). Χρησιμοποιείται για την αφαίρεση ενός κοντέινερ δεδομένων για να το κάνει να συμπεριφέρεται σαν επαναλαμβανόμενο αντικείμενο. Πιθανότατα χρησιμοποιείτε ήδη μερικά επαναλαμβανόμενα αντικείμενα κάθε μέρα: συμβολοσειρές, λίστες και λεξικά για να αναφέρετε μερικά.

Ο επαναληπτής ορίζεται από μια κλάση που εφαρμόζει το πρωτόκολλο Iterator . Το πρωτόκολλο αυτό αναζητά δύο μεθόδων μέσα στην τάξη: __iter__και __next__.

Ουάου, βήμα πίσω. Γιατί θα θέλατε να κάνετε επαναλήψεις;

Εξοικονόμηση χώρου μνήμης

Οι επαναληπτές δεν υπολογίζουν την τιμή κάθε στοιχείου όταν δημιουργούνται. Το υπολογίζουν μόνο όταν το ζητάτε. Αυτό είναι γνωστό ως τεμπέλη αξιολόγηση.

Η τεμπέλη αξιολόγηση είναι χρήσιμη όταν έχετε ένα πολύ μεγάλο σύνολο δεδομένων για υπολογισμό. Σας επιτρέπει να αρχίσετε να χρησιμοποιείτε τα δεδομένα αμέσως, ενώ υπολογίζεται ολόκληρο το σύνολο δεδομένων.

Ας πούμε ότι θέλουμε να πάρουμε όλους τους πρώτους αριθμούς που είναι μικρότεροι από έναν μέγιστο αριθμό.

Καθορίζουμε πρώτα τη συνάρτηση που ελέγχει εάν ένας αριθμός είναι πρωταρχικός:

def check_prime(number): for divisor in range(2, int(number ** 0.5) + 1): if number % divisor == 0: return False return True

Στη συνέχεια, ορίζουμε την κλάση επανάληψης που θα περιλαμβάνει τις μεθόδους __iter__και __next__:

class Primes: def __init__(self, max): self.max = max self.number = 1
 def __iter__(self): return self
 def __next__(self): self.number += 1 if self.number >= self.max: raise StopIteration elif check_prime(self.number): return self.number else: return self.__next__()

Primesδημιουργείται με μέγιστη τιμή. Εάν ο επόμενος πρωταρχικός είναι μεγαλύτερος ή ίσος από τον max, ο επαναληπτής θα δημιουργήσει μια StopIterationεξαίρεση, η οποία τελειώνει τον επαναληπτικό.

Όταν ζητάμε το επόμενο στοιχείο στον επαναληπτικό, θα αυξάνεται numberκατά 1 και θα ελέγχει αν είναι ένας πρώτος αριθμός. Εάν δεν είναι, θα καλέσει __next__ξανά έως ότου numberείναι πρωταρχικό. Μόλις είναι, ο επαναληπτής επιστρέφει τον αριθμό.

Χρησιμοποιώντας έναν επαναληπτικό, δεν δημιουργούμε μια λίστα πρωταρχικών αριθμών στη μνήμη μας. Αντ 'αυτού, δημιουργούμε τον επόμενο πρώτο αριθμό κάθε φορά που το ζητάμε.

Ας το δοκιμάσουμε:

primes = Primes(100000000000)
print(primes)
for x in primes: print(x)
---------
235711...

Κάθε επανάληψη του Primesαντικειμένου καλεί __next__για τη δημιουργία του επόμενου πρωταρχικού αριθμού.

Οι επαναληπτές μπορούν να επαναληφθούν μόνο μία φορά. Εάν προσπαθήσετε να επαναλάβετε primesξανά, δεν θα επιστραφεί καμία τιμή. Θα συμπεριφέρεται σαν μια κενή λίστα.

Τώρα που γνωρίζουμε τι είναι οι επαναληπτές και πώς να το κάνουμε, θα προχωρήσουμε στις γεννήτριες.

Γεννήτριες

Θυμηθείτε ότι οι λειτουργίες της γεννήτριας μας επιτρέπουν να δημιουργούμε επαναληπτικούς με πιο απλό τρόπο.

Οι γεννήτριες παρουσιάζουν τη yieldδήλωση στην Python. Λειτουργεί λίγο returnγιατί επιστρέφει μια τιμή.

Η διαφορά είναι ότι σώζει την κατάσταση της συνάρτησης. Την επόμενη φορά που θα κληθεί η συνάρτηση, η εκτέλεση συνεχίζεται από εκεί που σταμάτησε , με τις ίδιες μεταβλητές τιμές που είχε πριν από την απόδοση.

Εάν μετατρέψουμε τον Primesεπαναληπτή μας σε γεννήτρια, θα μοιάζει με αυτό:

def Primes(max): number = 1 while number < max: number += 1 if check_prime(number): yield number
primes = Primes(100000000000)
print(primes)
for x in primes: print(x)
---------
235711...

Τώρα αυτό είναι αρκετά πυθικό! Μπορούμε να κάνουμε καλύτερα;

Ναί! Μπορούμε να χρησιμοποιήσουμε το Generator Expressions , που παρουσιάστηκε με το PEP 289.

Αυτό είναι το ισοδύναμο κατανόησης λίστας των γεννητριών. Λειτουργεί ακριβώς με τον ίδιο τρόπο όπως μια κατανόηση λίστας, αλλά η έκφραση περιβάλλεται με ()αντίθεση με [].

Η ακόλουθη έκφραση μπορεί να αντικαταστήσει τη λειτουργία γεννήτριας παραπάνω:

primes = (i for i in range(2, 100000000000) if check_prime(i))
print(primes)
for x in primes: print(x)
---------

    
     235711...
    

Αυτή είναι η ομορφιά των γεννητριών στο Python.

Συνοψίζοντας…

  • Οι γεννήτριες σας επιτρέπουν να δημιουργήσετε επαναληπτικούς με πολύ πυθικό τρόπο.
  • Οι επαναληπτές επιτρέπουν τεμπέλης αξιολόγηση, δημιουργώντας μόνο το επόμενο στοιχείο ενός επαναλαμβανόμενου αντικειμένου όταν ζητηθεί. Αυτό είναι χρήσιμο για πολύ μεγάλα σύνολα δεδομένων.
  • Οι επαναληπτές και οι γεννήτριες μπορούν να επαναληφθούν μόνο μία φορά.
  • Οι λειτουργίες της γεννήτριας είναι καλύτερες από τις επαναληπτικές.
  • Οι εκφράσεις Generator είναι καλύτερες από τις Iterators (μόνο για απλές περιπτώσεις).

Μπορείτε επίσης να δείτε την εξήγησή μου σχετικά με τον τρόπο με τον οποίο χρησιμοποίησα το Python για να βρω ενδιαφέροντα άτομα για παρακολούθηση στο Medium.

Για περισσότερες ενημερώσεις, ακολουθήστε με στο Twitter.