Python-tuotto, generaattorit ja generaattorilausekkeet

Tässä opetusohjelmassa opit luomaan iteraatioita helposti Python-generaattoreilla, kuinka se eroaa iteraattoreista ja normaaleista toiminnoista, ja miksi sinun pitäisi käyttää sitä.

Video: Python-generaattorit

Generaattorit Pythonissa

Iteraattorin rakentamisessa Pythonissa on paljon työtä. Meidän on toteutettava luokka __iter__()ja __next__()menetelmä, seurattava sisäisiä tiloja ja korotettava, StopIterationkun palautettavia arvoja ei ole.

Tämä on sekä pitkä että vasta-intuitiivinen. Generaattori tulee apuun tällaisissa tilanteissa.

Python-generaattorit ovat yksinkertainen tapa luoda iteraattoreita. Pythonin generaattorit käsittelevät kaiken edellä mainitun työn automaattisesti.

Yksinkertaisesti sanottuna generaattori on funktio, joka palauttaa objektin (iteraattorin), jonka voimme iteroida (yksi arvo kerrallaan).

Luo generaattoreita Pythonissa

Generaattorin luominen Pythoniin on melko helppoa. Se on yhtä helppoa kuin normaalin funktion määritteleminen, mutta yieldlauseella lauseen sijaan return.

Jos funktio sisältää ainakin yhden yieldlauseen (se voi sisältää muita yieldtai returnlauseita), siitä tulee generaattorifunktio. Molemmat yieldja returnpalauttavat jonkin arvon funktiosta.

Erona on, että vaikka returnkäsky lopettaa toiminnon kokonaan, yieldlause keskeyttää toiminnon, joka tallentaa kaikki tilansa, ja jatkuu myöhemmin sieltä peräkkäisissä puheluissa.

Generaattorin ja normaalitoiminnon erot

Näin generaattoritoiminto eroaa normaalista toiminnosta.

  • Generaattori-toiminto sisältää yhden tai useamman yieldlauseen.
  • Kun sitä kutsutaan, se palauttaa objektin (iteraattorin), mutta ei aloita suoritusta heti.
  • Menetelmät pitävät __iter__()ja __next__()toteutetaan automaattisesti. Joten voimme toistaa kohteiden läpi käyttämällä next().
  • Kun toiminto tuottaa, toiminto keskeytetään ja ohjaus siirretään soittajalle.
  • Paikalliset muuttujat ja niiden tilat muistetaan peräkkäisten puhelujen välillä.
  • Lopuksi, kun toiminto päättyy, StopIterationse nousee automaattisesti uusissa puheluissa.

Tässä on esimerkki kaikkien edellä mainittujen kohtien havainnollistamiseksi. Meillä on generaattoritoiminto, joka on nimetty my_gen()useilla yieldlauseilla.

 # A simple generator function def my_gen(): n = 1 print('This is printed first') # Generator function contains yield statements yield n n += 1 print('This is printed second') yield n n += 1 print('This is printed at last') yield n

Tulkin vuorovaikutteinen ajo on annettu alla. Suorita nämä Python-kuoressa nähdäksesi tuotoksen.

 >>> # It returns an object but does not start execution immediately. >>> a = my_gen() >>> # We can iterate through the items using next(). >>> next(a) This is printed first 1 >>> # Once the function yields, the function is paused and the control is transferred to the caller. >>> # Local variables and theirs states are remembered between successive calls. >>> next(a) This is printed second 2 >>> next(a) This is printed at last 3 >>> # Finally, when the function terminates, StopIteration is raised automatically on further calls. >>> next(a) Traceback (most recent call last):… StopIteration >>> next(a) Traceback (most recent call last):… StopIteration

Yksi mielenkiintoinen asia huomata yllä olevassa esimerkissä on, että muuttujan n arvo muistetaan jokaisen puhelun välillä.

Toisin kuin normaalit funktiot, paikallisia muuttujia ei tuhota, kun funktio tuottaa. Lisäksi generaattoriobjekti voidaan toistaa vain kerran.

Prosessin uudelleenkäynnistämiseksi meidän on luotava toinen generaattoriobjekti käyttämällä jotain sellaista a = my_gen().

Viimeinen asia on huomata, että voimme käyttää generaattoreita silmukoihin suoraan.

Tämä johtuu siitä, että forsilmukka ottaa iteraattorin ja iteroi sen yli next()toiminnon avulla. Se päättyy automaattisesti, kun StopIterationse nostetaan. Tarkista tästä, kuinka for -silmukka todella toteutetaan Pythonissa.

 # A simple generator function def my_gen(): n = 1 print('This is printed first') # Generator function contains yield statements yield n n += 1 print('This is printed second') yield n n += 1 print('This is printed at last') yield n # Using for loop for item in my_gen(): print(item)

Kun suoritat ohjelmaa, tulos on:

 Tämä tulostetaan ensin 1 Tämä tulostetaan toinen 2 Tämä tulostetaan viimeisenä 3

Python-generaattorit, joissa on silmukka

Yllä olevasta esimerkistä on vähemmän hyötyä, ja tutkimme sitä vain saadaksemme kuvan taustalla tapahtuvasta.

Normaalisti generaattoritoiminnot toteutetaan silmukalla, jolla on sopiva loppuehto.

Otetaan esimerkki generaattorista, joka kääntää merkkijonon.

 def rev_str(my_str): length = len(my_str) for i in range(length - 1, -1, -1): yield my_str(i) # For loop to reverse the string for char in rev_str("hello"): print(char)

Tuotos

 olleh

Tässä esimerkissä olemme käyttäneet range()funktiota hakemaan indeksi päinvastaisessa järjestyksessä for-silmukan avulla.

Huomaa : Tämä generaattoritoiminto ei toimi vain merkkijonojen, vaan myös muun tyyppisten iterable-tiedostojen, kuten luettelon, dupleksin jne. Kanssa.

Python-generaattorin lauseke

Yksinkertaisia ​​generaattoreita voidaan luoda helposti lennossa generaattorilausekkeilla. Se tekee generaattorien rakentamisesta helppoa.

Samoin kuin lambda-funktiot, jotka luovat nimettömiä toimintoja, generaattorin lausekkeet luovat nimettömiä generaattorin toimintoja.

Generaattorin lausekkeen syntakse on samanlainen kuin Pythonin luettelon ymmärtäminen. Mutta hakasulkeet korvataan pyöreillä sulkeilla.

Suurin ero luettelon ymmärtämisen ja generaattorin lausekkeen välillä on se, että luettelon ymmärtäminen tuottaa koko luettelon, kun taas generaattorin lauseke tuottaa yhden kohteen kerrallaan.

They have lazy execution ( producing items only when asked for ). For this reason, a generator expression is much more memory efficient than an equivalent list comprehension.

 # Initialize the list my_list = (1, 3, 6, 10) # square each term using list comprehension list_ = (x**2 for x in my_list) # same thing can be done using a generator expression # generator expressions are surrounded by parenthesis () generator = (x**2 for x in my_list) print(list_) print(generator)

Output

 (1, 9, 36, 100) 

We can see above that the generator expression did not produce the required result immediately. Instead, it returned a generator object, which produces items only on demand.

Here is how we can start getting items from the generator:

 # Initialize the list my_list = (1, 3, 6, 10) a = (x**2 for x in my_list) print(next(a)) print(next(a)) print(next(a)) print(next(a)) next(a)

When we run the above program, we get the following output:

 1 9 36 100 Traceback (most recent call last): File "", line 15, in StopIteration

Generator expressions can be used as function arguments. When used in such a way, the round parentheses can be dropped.

 >>> sum(x**2 for x in my_list) 146 >>> max(x**2 for x in my_list) 100

Use of Python Generators

There are several reasons that make generators a powerful implementation.

1. Easy to Implement

Generators can be implemented in a clear and concise way as compared to their iterator class counterpart. Following is an example to implement a sequence of power of 2 using an iterator class.

 class PowTwo: def __init__(self, max=0): self.n = 0 self.max = max def __iter__(self): return self def __next__(self): if self.n> self.max: raise StopIteration result = 2 ** self.n self.n += 1 return result

The above program was lengthy and confusing. Now, let's do the same using a generator function.

 def PowTwoGen(max=0): n = 0 while n < max: yield 2 ** n n += 1

Since generators keep track of details automatically, the implementation was concise and much cleaner.

2. Memory Efficient

A normal function to return a sequence will create the entire sequence in memory before returning the result. This is an overkill, if the number of items in the sequence is very large.

Generator implementation of such sequences is memory friendly and is preferred since it only produces one item at a time.

3. Represent Infinite Stream

Generaattorit ovat erinomaisia ​​välineitä edustamaan ääretöntä datavirtaa. Äärettömiä virtoja ei voida tallentaa muistiin, ja koska generaattorit tuottavat vain yhden kohteen kerrallaan, ne voivat edustaa ääretöntä datavirtaa.

Seuraava generaattoritoiminto voi tuottaa kaikki parilliset luvut (ainakin teoriassa).

 def all_even(): n = 0 while True: yield n n += 2

4. Putkilinjan generaattorit

Useita generaattoreita voidaan käyttää sarjaan toimintoja. Tämä voidaan parhaiten havainnollistaa esimerkin avulla.

Oletetaan, että meillä on generaattori, joka tuottaa Fibonacci-sarjan numerot. Ja meillä on toinen generaattori numeroiden neliöimiseksi.

Jos haluamme selvittää Fibonacci-sarjan numeroiden neliösumman, voimme tehdä sen seuraavalla tavalla yhdistämällä generaattoritoimintojen lähtö yhteen.

 def fibonacci_numbers(nums): x, y = 0, 1 for _ in range(nums): x, y = y, x+y yield x def square(nums): for num in nums: yield num**2 print(sum(square(fibonacci_numbers(10))))

Tuotos

 4895

Tämä putkilinja on tehokas ja helppo lukea (ja kyllä, paljon viileämpi!).

Mielenkiintoisia artikkeleita...