Pythoni saagis, generaatorid ja generaatori avaldised

Selles õpetuses saate teada, kuidas Pythoni generaatorite abil hõlpsasti iteratsioone luua, kuidas see erineb iteraatoritest ja tavalistest funktsioonidest ning miks peaksite seda kasutama.

Video: Pythoni generaatorid

Generaatorid Pythonis

Pythonis on palju tööd iteraatori ehitamisel. Peame rakendama klassi __iter__()ja __next__()meetodi, jälgima sisemisi olekuid ja tõstma, StopIterationkui pole väärtusi, mida tagastada.

See on nii pikk kui ka vastupidine. Sellistes olukordades tuleb appi generaator.

Pythoni generaatorid on lihtne kordajate loomise viis. Kogu ülalnimetatud tööga tegelevad Pythoni generaatorid automaatselt.

Lihtsamalt öeldes on generaator funktsioon, mis tagastab objekti (iteraator), mille abil saame itereerida (üks väärtus korraga).

Generaatorite loomine Pythonis

Pythonis on generaatori loomine üsna lihtne. See on sama lihtne kui tavalise funktsiooni määratlemine, kuid yieldavalduse asemel returnlausega.

Kui funktsioon sisaldab vähemalt ühte yieldlauset (see võib sisaldada muud yieldvõi returnlauset), saab sellest generaatorfunktsioon. Mõlemad yieldja returntagastavad funktsioonilt mingi väärtuse.

Erinevus seisneb selles, et kui returnlause lõpetab funktsiooni täielikult, yieldpeatab lause funktsiooni kõigi olekute salvestamise ja jätkab sealt edasi järjestikuste kõnedega.

Generaatori funktsiooni ja normaalfunktsiooni erinevused

Nii erineb generaatori funktsioon tavalisest funktsioonist.

  • Generaatori funktsioon sisaldab ühte või mitut yieldlauset.
  • Kui see kutsutakse, tagastab see objekti (iteraator), kuid ei alusta käivitamist kohe.
  • Meetodid meeldivad __iter__()ja __next__()rakendatakse automaatselt. Nii et saame üksuste abil itereerida next().
  • Kui funktsioon annab järele, peatatakse funktsioon ja juhtimine antakse üle helistajale.
  • Kohalikud muutujad ja nende olekud jäävad järjestikuste kõnede vahel meelde.
  • Lõpuks, kui funktsioon lõpetatakse, StopIterationtõstetakse see edasiste kõnede korral automaatselt.

Siin on näide kõigi ülaltoodud punktide illustreerimiseks. Meil on generaatori funktsioon, mille nimi my_gen()on mitu yieldlauset.

 # 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

Interaktiivne jooksu tõlk on toodud allpool. Väljundi nägemiseks käivitage need Pythoni kestas.

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

Üks huvitav asi, mida ülaltoodud näites tähele panna, on see, et muutuja n väärtus jääb iga kõne vahele meelde.

Erinevalt tavalistest funktsioonidest ei hävitata lokaalseid muutujaid funktsiooni andmisel. Lisaks saab generaatori objekti itereerida ainult üks kord.

Protsessi taaskäivitamiseks peame looma teise generaatori objekti, kasutades midagi sellist a = my_gen().

Viimane asi, mida tuleb märkida, on see, et generaatorit saab kasutada otse silmuste jaoks.

Selle põhjuseks on asjaolu, et forsilmus võtab iteraatori ja itereerib selle next()funktsiooni abil. See lõpeb automaatselt, kui see StopIterationon üles tõstetud. Siit saate teada, kuidas for loopi Pythonis tegelikult rakendatakse.

 # 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)

Programmi käivitamisel on väljund järgmine:

 See prinditakse kõigepealt 1 See trükitakse teine ​​2 See trükitakse lõpuks 3

Silmusega Pythoni generaatorid

Ülaltoodud näide on vähem kasutatav ja uurisime seda lihtsalt selleks, et saada aimu taustal toimuvast.

Tavaliselt viiakse generaatori funktsioonid ellu sobiva lõpptingimusega silmusega.

Võtame näite generaatorist, mis muudab stringi ümber.

 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)

Väljund

 olleh

Selles näites oleme range()funktsiooni kasutanud indeksi saamiseks vastupidises järjekorras for loopi abil.

Märkus . See generaatori funktsioon ei tööta mitte ainult stringidega, vaid ka muud tüüpi iterable'idega, nagu loend, dupleks jne.

Pythoni generaatori avaldis

Lihtsaid generaatoreid saab hõlpsalt luua generaatorilausete abil. See muudab generaatorite ehitamise lihtsaks.

Sarnaselt lambda funktsioonidele, mis loovad anonüümseid funktsioone, loovad generaatori avaldised anonüümsed generaatori funktsioonid.

Generaatori avaldise süntaks sarnaneb Pythoni loendi mõistmise omaga. Kuid nurksulgudes asendatakse ümarad sulgud.

Peamine erinevus loendi mõistmise ja generaatori avaldise vahel seisneb selles, et loendi mõistmine loob kogu loendi, samal ajal kui generaatori avaldis toodab ühe üksuse korraga.

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

Generaatorid on suurepärased meediumid, et esindada lõpmatut andmevoogu. Lõpmatuid vooge ei saa mällu salvestada ja kuna generaatorid toodavad korraga ainult ühte üksust, võivad nad esindada lõpmatut andmevoogu.

Järgmine generaatori funktsioon võib genereerida kõik paarisarvud (vähemalt teoreetiliselt).

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

4. Torujuhtmete generaatorid

Mitme generaatori abil saab rida toiminguid juhtida. Seda saab kõige paremini illustreerida näite abil.

Oletame, et meil on generaator, mis toodab Fibonacci seeria numbreid. Ja meil on veel üks generaator numbrite ruutude tegemiseks.

Kui soovime välja selgitada Fibonacci seeria arvude ruutude summa, saame seda teha järgmisel viisil, ühendades generaatorfunktsioonide väljundid kokku.

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

Väljund

 4895

See torustik on tõhus ja hõlpsasti loetav (ja jah, palju lahedam!).

Huvitavad Artiklid...