Hvordan - og hvorfor - skal du bruge Python Generators

Generatorer har været en vigtig del af Python lige siden de blev introduceret med PEP 255.

Generatorfunktioner giver dig mulighed for at erklære en funktion, der opfører sig som en iterator.

De tillader programmører at lave en iterator på en hurtig, nem og ren måde.

Hvad er en iterator, kan du spørge?

En iterator er et objekt, der kan gentages (loopes) efter. Det bruges til at abstrahere en beholder med data for at få det til at opføre sig som et gentageligt objekt. Du bruger sandsynligvis allerede et par gentagelige objekter hver dag: strenge, lister og ordbøger for at nævne nogle få.

En iterator defineres af en klasse, der implementerer Iterator-protokollen . Denne protokol søger efter to metoder inden for klassen: __iter__og __next__.

Whoa, gå tilbage. Hvorfor vil du endda lave iteratorer?

Sparer hukommelsesplads

Iteratorer beregner ikke værdien af ​​hvert element, når det instantieres. De beregner det kun, når du beder om det. Dette er kendt som doven evaluering.

Lazy evaluering er nyttig, når du har et meget stort datasæt at beregne. Det giver dig mulighed for at begynde at bruge dataene med det samme, mens hele datasættet beregnes.

Lad os sige, at vi vil have alle primtal, der er mindre end et maksimalt antal.

Vi definerer først den funktion, der kontrollerer, om et tal er prime:

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

Derefter definerer vi iteratorklassen, der inkluderer __iter__og __next__metoderne:

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__()

Primesinstantieres med en maksimal værdi. Hvis den næste prime er større eller lig end the max, hæver iteratoren en StopIterationundtagelse, som slutter iteratoren.

Når vi anmoder om det næste element i iteratoren, øges det numbermed 1 og kontrollerer, om det er et primtal. Hvis det ikke er det, ringer det __next__igen, indtil det numberer prime. Når det er tilfældet, returnerer iteratoren tallet.

Ved at bruge en iterator opretter vi ikke en liste med primtal i vores hukommelse. I stedet genererer vi det næste primtal, hver gang vi anmoder om det.

Lad os prøve det:

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

Hver iteration af Primesobjektet kalder for __next__at generere det næste primtal.

Iteratorer kan kun gentages én gang. Hvis du prøver at gentage primesigen, returneres ingen værdi. Det opfører sig som en tom liste.

Nu hvor vi ved, hvad iteratorer er, og hvordan man laver en, går vi videre til generatorer.

Generatorer

Husk at generatorfunktioner giver os mulighed for at oprette iteratorer på en mere enkel måde.

Generatorer introducerer yielderklæringen til Python. Det virker lidt som returnfordi det returnerer en værdi.

Forskellen er, at det gemmer funktionens tilstand . Næste gang funktionen kaldes, fortsætter udførelsen fra det sted , hvor den slap med de samme variable værdier, som den havde, før den gav.

Hvis vi omdanner vores Primesiterator til en generator, vil det se sådan ud:

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...

Nu er det ret pythonisk! Kan vi gøre det bedre?

Ja! Vi kan bruge Generator Expressions , introduceret med PEP 289.

Dette er den listeforståelsesækvivalent med generatorer. Det fungerer nøjagtigt på samme måde som en listeforståelse, men udtrykket er omgivet med ()i modsætning til [].

Følgende udtryk kan erstatte vores generatorfunktion ovenfor:

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

    
     235711...
    

Dette er skønheden ved generatorer i Python.

Sammenfattende ...

  • Generatorer giver dig mulighed for at oprette iteratorer på en meget pythonisk måde.
  • Iteratorer tillader doven evaluering og genererer kun det næste element i et iterabelt objekt, når der anmodes om det. Dette er nyttigt til meget store datasæt.
  • Iteratorer og generatorer kan kun gentages én gang.
  • Generatorfunktioner er bedre end Iteratorer.
  • Generatorudtryk er bedre end Iteratorer (kun i simple tilfælde).

Du kan også tjekke min forklaring på, hvordan jeg brugte Python til at finde interessante mennesker at følge på Medium.

For flere opdateringer, følg mig på Twitter.