Industriële fabricage
Industrieel internet der dingen | Industriële materialen | Onderhoud en reparatie van apparatuur | Industriële programmering |
home  MfgRobots >> Industriële fabricage >  >> Industrial programming >> Python

Python-generatoren

Python-generatoren

In deze tutorial leer je hoe je eenvoudig iteraties kunt maken met Python-generators, hoe het verschilt van iterators en normale functies en waarom je het zou moeten gebruiken.

Video:Python-generatoren

Generatoren in Python

Er is veel werk aan het bouwen van een iterator in Python. We moeten een klasse implementeren met __iter__() en __next__() methode, houd interne toestanden bij en verhoog StopIteration wanneer er geen waarden kunnen worden geretourneerd.

Dit is zowel langdradig als contra-intuïtief. Generator komt in dergelijke situaties te hulp.

Python-generatoren zijn een eenvoudige manier om iterators te maken. Al het werk dat we hierboven noemden wordt automatisch afgehandeld door generatoren in Python.

Simpel gezegd, een generator is een functie die een object (iterator) retourneert waarover we kunnen herhalen (één waarde per keer).


Generatoren maken in Python

Het is vrij eenvoudig om een ​​generator te maken in Python. Het is net zo eenvoudig als het definiëren van een normale functie, maar met een yield statement in plaats van een return verklaring.

Als een functie ten minste één yield . bevat statement (het kan andere yield bevatten of return statements), wordt het een generatorfunctie. Beide yield en return zal een waarde uit een functie teruggeven.

Het verschil is dat terwijl een return statement beëindigt een functie volledig, yield statement pauzeert de functie en bewaart alle statussen en gaat van daaruit verder bij opeenvolgende aanroepen.


Verschillen tussen generatorfunctie en normale functie

Hier is hoe een generatorfunctie verschilt van een normale functie.

  • Generatorfunctie bevat een of meer yield verklaringen.
  • Wanneer het wordt aangeroepen, retourneert het een object (iterator) maar start de uitvoering niet onmiddellijk.
  • Methoden zoals __iter__() en __next__() worden automatisch uitgevoerd. Dus we kunnen de items herhalen met behulp van next() .
  • Zodra de functie resultaat geeft, wordt de functie gepauzeerd en wordt de controle overgedragen aan de beller.
  • Lokale variabelen en hun status worden onthouden tussen opeenvolgende aanroepen.
  • Ten slotte, wanneer de functie eindigt, StopIteration wordt automatisch verhoogd bij verdere oproepen.

Hier is een voorbeeld om alle bovengenoemde punten te illustreren. We hebben een generatorfunctie genaamd my_gen() met meerdere yield verklaringen.

# 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

Een interactieve run in de interpreter wordt hieronder gegeven. Voer deze uit in de Python-shell om de uitvoer te zien.

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

Een interessant ding om op te merken in het bovenstaande voorbeeld is dat de waarde van variabele n wordt tussen elke oproep onthouden.

In tegenstelling tot normale functies worden de lokale variabelen niet vernietigd wanneer de functie meegeeft. Bovendien kan het generatorobject slechts één keer worden herhaald.

Om het proces opnieuw te starten, moeten we een ander generatorobject maken met iets als a = my_gen() .

Een laatste ding om op te merken is dat we generatoren direct met for-lussen kunnen gebruiken.

Dit komt omdat een for loop neemt een iterator en itereert erover met next() functie. Het eindigt automatisch wanneer StopIteration wordt verhoogd. Kijk hier om te weten hoe een for-lus in Python wordt geïmplementeerd.

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

Wanneer u het programma uitvoert, is de uitvoer:

This is printed first
1
This is printed second
2
This is printed at last
3

Python-generatoren met een lus

Het bovenstaande voorbeeld is van minder nut en we hebben het bestudeerd om een ​​idee te krijgen van wat er op de achtergrond gebeurde.

Normaal gesproken worden generatorfuncties geïmplementeerd met een lus met een geschikte afsluitconditie.

Laten we een voorbeeld nemen van een generator die een string omkeert.

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)

Uitvoer

o
l
l
e
h

In dit voorbeeld hebben we de range() . gebruikt functie om de index in omgekeerde volgorde te krijgen met behulp van de for-lus.

Opmerking :Deze generatorfunctie werkt niet alleen met strings, maar ook met andere soorten iterables zoals list, tuple, etc.


Python Generator-expressie

Eenvoudige generatoren kunnen gemakkelijk on-the-fly worden gemaakt met behulp van generator-uitdrukkingen. Het maakt het bouwen van generatoren eenvoudig.

Net als de lambda-functies die anonieme functies creëren, creëren generator-expressies anonieme generatorfuncties.

De syntaxis voor generatorexpressie is vergelijkbaar met die van een lijstbegrip in Python. Maar de vierkante haken zijn vervangen door ronde haakjes.

Het belangrijkste verschil tussen een lijstbegrip en een generatoruitdrukking is dat een lijstbegrip de hele lijst produceert, terwijl de generatoruitdrukking één item tegelijk produceert.

Ze hebben een luie uitvoering (alleen items produceren wanneer daarom wordt gevraagd). Om deze reden is een generatoruitdrukking veel geheugenefficiënter dan een equivalent lijstbegrip.

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

Uitvoer

[1, 9, 36, 100]
<generator object <genexpr> at 0x7f5d4eb4bf50>

We kunnen hierboven zien dat de generatoruitdrukking niet onmiddellijk het vereiste resultaat opleverde. In plaats daarvan retourneerde het een generatorobject, dat alleen op verzoek items produceert.

Hier is hoe we items uit de generator kunnen halen:

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

Wanneer we het bovenstaande programma uitvoeren, krijgen we de volgende uitvoer:

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

Generator-expressies kunnen worden gebruikt als functieargumenten. Bij gebruik op een dergelijke manier kunnen de ronde haakjes worden verwijderd.

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

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

Gebruik van Python-generatoren

Er zijn verschillende redenen die generatoren tot een krachtige implementatie maken.

1. Eenvoudig te implementeren

Generatoren kunnen op een duidelijke en beknopte manier worden geïmplementeerd in vergelijking met hun tegenhanger van de iteratorklasse. Hieronder volgt een voorbeeld om een ​​reeks van macht van 2 te implementeren met behulp van een iteratorklasse.

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

Bovenstaand programma was lang en verwarrend. Laten we nu hetzelfde doen met een generatorfunctie.

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

Omdat generatoren automatisch details bijhouden, was de implementatie beknopt en veel schoner.

2. Geheugen efficiënt

Een normale functie om een ​​reeks terug te geven, zal de hele reeks in het geheugen maken voordat het resultaat wordt geretourneerd. Dit is een overkill als het aantal items in de reeks erg groot is.

Generator-implementatie van dergelijke reeksen is geheugenvriendelijk en heeft de voorkeur omdat het slechts één item tegelijk produceert.

3. Vertegenwoordig oneindige stroom

Generatoren zijn uitstekende media om een ​​oneindige stroom gegevens weer te geven. Oneindige streams kunnen niet in het geheugen worden opgeslagen en aangezien generatoren slechts één item tegelijk produceren, kunnen ze een oneindige datastroom vertegenwoordigen.

De volgende generatorfunctie kan alle even getallen genereren (althans in theorie).

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

4. Generatoren voor pijpleidingen

Meerdere generatoren kunnen worden gebruikt om een ​​reeks bewerkingen te pijplijnen. Dit wordt het best geïllustreerd aan de hand van een voorbeeld.

Stel dat we een generator hebben die de getallen in de Fibonacci-reeks produceert. En we hebben nog een generator voor het kwadrateren van getallen.

Als we de som van de kwadraten van getallen in de Fibonacci-reeks willen weten, kunnen we dat op de volgende manier doen door de output van generatorfuncties samen te pijplijnen.

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

Uitvoer

4895

Deze pipelining is efficiënt en gemakkelijk te lezen (en ja, een stuk cooler!).


Python

  1. Python-operators
  2. Python-functieargumenten
  3. Python-woordenboek
  4. Python-generatoren
  5. Python-sluitingen
  6. Python-decorateurs
  7. Python Lambda-functies met VOORBEELDEN
  8. Python abs() Functie:Voorbeelden van absolute waarden
  9. Python round() functie met VOORBEELDEN
  10. Python map() functie met VOORBEELDEN
  11. Opbrengst in Python-zelfstudie:voorbeeld van generator en rendement versus rendement