Vra

Ek is besig om te leer Python en ek het oor kragopwekker funksies, diegene wat 'n opbrengs verklaring het in hulle gekom. Ek wil weet watter tipe probleme wat hierdie funksies is regtig 'n goeie by die oplossing van.

Was dit nuttig?

Oplossing

Generators gee jy lui evaluering. Jy gebruik dit deur iterating oor hulle, óf uitdruklik met 'vir' of implisiet deur dit aan 'n funksie of bou wat iterate. Jy kan dink van kragopwekkers as die terugkeer van verskeie items, asof hulle 'n lys terugkeer, maar in plaas van hulle almal by sy terugkeer nadat hulle terugkeer hulle een-vir-een, en die kragopwekker funksie is gestop totdat die volgende item versoek word.

Generators is goed vir die berekening van groot stelle resultate (in die besonder die volgende behels lusse hulself) waar jy weet nie of jy gaan al die resultate nodig, of waar jy nie wil hê om die geheue toeken vir alle resultate aan Die selfde tyd. Of vir situasies waar die kragopwekker gebruik 'n ander kragopwekker, of verbruik 'n ander bron, en dit is meer gerieflik as dit gebeur so laat as moontlik te maak.

Nog 'n gebruik vir kragopwekkers (dit is regtig dieselfde) is om verifikasie te vervang met iterasie. In sommige gevalle wil jy 'n funksie om 'n baie werk te doen en soms rapporteer terug aan die oproeper. Tradisioneel jy wil 'n terugbel funksie te gebruik vir hierdie. Jy slaag hierdie terugbel om die werk-funksie en dit sal van tyd tot tyd noem dit terugbel. Die kragopwekker benadering is dat die werk-funksie (nou 'n kragopwekker) weet niks van die terugbel, en net oplewer wanneer dit wil iets aan te meld. Die oproeper, in plaas van die skryf van 'n aparte terugbel en verby dat die werk-funksie, doen al die verslagdoening werk in 'n bietjie vir 'n lus om die kragopwekker.

Byvoorbeeld, sê jy 'n program 'lêerstelsel soek' geskryf. Jy kan die search in sy geheel uit te voer, versamel die resultate en dan wys hulle een op 'n slag. Al die resultate sou hê om afgehaal word voordat jy die eerste het, en al die resultate sal in die geheue wees op dieselfde tyd. Of jy kan die resultate te vertoon terwyl jy hulle vind, wat meer geheue doeltreffende en baie vriendeliker teenoor die gebruiker sal wees. Laasgenoemde kan gedoen word deur die verbygaan van die resultaat-drukwerk funksie om die lêerstelsel-soekfunksie, of dit kan gedoen word deur net die maak van die search funksie 'n kragopwekker en iterating oor die uitslag.

As jy wil 'n voorbeeld van die twee laasgenoemde benaderings sien, sien os.path.walk () (die ou lêerstelsel-stap funksie met terugbel) en os.walk () (die nuwe lêerstelsel-stap kragopwekker.) Van natuurlik, as jy regtig wou al resultate in 'n lys in te samel, die kragopwekker benadering is triviale om te skakel na die groot-lys benadering:

big_list = list(the_generator)

Ander wenke

Een van die redes vir kragopwekker gebruik is om die oplossing duideliker te maak vir 'n soort van oplossings.

Die ander is om te behandel resultate een op 'n tyd, vermy die bou van groot lyste van resultate wat jy in elk geval sou proses geskei.

As jy 'n Fibonacci-up-to-N funksie soos volg:

# function version
def fibon(n):
    a = b = 1
    result = []
    for i in xrange(n):
        result.append(a)
        a, b = b, a + b
    return result

Jy kan meer maklik skryf die funksie as hierdie:

# generator version
def fibon(n):
    a = b = 1
    for i in xrange(n):
        yield a
        a, b = b, a + b

Die funksie is duideliker. En as jy die funksie te gebruik soos volg:

for x in fibon(1000000):
    print x,

in hierdie voorbeeld, as die gebruik van die kragopwekker weergawe, die hele 1000000 item lys sal nie geskep word nie, net een waarde op 'n slag. Dit sou nie die geval wees wanneer die gebruik van die lys weergawe, waar 'n lys eerste geskep sal word.

Sien die artikel "Motivering" in PEP 255 .

'n nie-ooglopende gebruik van kragopwekkers is die skep van breekbare funksies, wat kan jy doen soos werk UI of hardloop 'n paar werksgeleenthede "gelyktydig" (Interleaved, eintlik) terwyl dit nie die gebruik van drade.

Ek vind hierdie verduideliking wat my twyfel goedkeuring. Want daar is 'n moontlikheid dat iemand wat nie Generators weet ook nie weet nie yield

Terug

Die terugkeer stelling is waar al die plaaslike veranderlikes vernietig en die gevolglike waarde is teruggegee (teruggestuur) aan die oproeper. Moet dieselfde funksie genoem 'n geruime tyd later, sal die funksie 'n vars nuwe stel veranderlikes kry.

Yield

Maar wat as die plaaslike veranderlikes nie weggegooi toe ons 'n funksie te verlaat? Dit impliseer dat ons kan resume the function waar ons opgehou het. Dit is hier waar die konsep van generators word bekendgestel en die yield verklaring hervat waar die function opgehou het.

  def generate_integers(N):
    for i in xrange(N):
    yield i

    In [1]: gen = generate_integers(3)
    In [2]: gen
    <generator object at 0x8117f90>
    In [3]: gen.next()
    0
    In [4]: gen.next()
    1
    In [5]: gen.next()

So wat is die verskil tussen return en yield state in Python.

Yield stelling wat 'n funksie van 'n kragopwekker funksie maak.

So kragopwekkers is 'n eenvoudige en kragtige instrument vir die skep van iterators. Dit is beskrywe soos gereelde funksies, maar hulle gebruik die yield verklaring wanneer hulle wil om data terug te keer. Elke keer as volgende () genoem word, die kragopwekker hervat waar dit opgehou het (dit onthou al datawaardes die en wat verklaring verlede uitgevoer is).

Real World Voorbeeld

Kom ons sê jy het as 100 miljoen domeine in jou MySQL tabel, en jy wil graag Alexa rank te werk vir elke domein.

Eerste ding wat jy nodig het, is om jou domein naam te kies uit die databasis.

Kom ons sê jou tafel naam is domains en naam kolom is domain.

As jy gebruik SELECT domain FROM domains dit gaan tot 100 miljoen rye wat gaan baie geheue verbruik terugkeer. So jou bediener kan loop.

So jy besluit om die program in groepe loop. Kom ons sê ons lot grootte is 1000.

In ons eerste groep sal ons navraag die eerste 1000 rye, kyk Alexa rank vir elke domein en die databasis ry by te werk.

In ons tweede batch sal ons werk op die volgende 1000 rye. In ons derde party dit sal wees 2001-3000 en so aan.

Nou moet ons 'n kragopwekker funksie wat ons groepe genereer.

Hier is ons kragopwekker funksie:

def ResultGenerator(cursor, batchsize=1000):
    while True:
        results = cursor.fetchmany(batchsize)
        if not results:
            break
        for result in results:
            yield result

Soos jy kan sien, ons funksie hou yielding die resultate. As jy die navraag return in plaas van yield gebruik, dan is die hele funksie sou geëindig nadat dit terugkeer bereik.

return - returns only once
yield - returns multiple times

As 'n funksie maak gebruik van die term yield dan is dit 'n kragopwekker.

Nou kan jy herhaal soos volg:

db = MySQLdb.connect(host="localhost", user="root", passwd="root", db="domains")
cursor = db.cursor()
cursor.execute("SELECT domain FROM domains")
for result in ResultGenerator(cursor):
    doSomethingWith(result)
db.close()

buffer. Wanneer dit doeltreffend om data in groot dele haal, maar verwerk dit in klein stukkies, dan 'n kragopwekker kan help:

def bufferedFetch():
  while True:
     buffer = getBigChunkOfData()
     # insert some code to break on 'end of data'
     for i in buffer:    
          yield i

Die bogenoemde kan jy maklik aparte buffer van verwerking. Die funksie verbruiker kan nou net die waardes een vir een sonder om bekommerd te wees oor buffer.

Ek het gevind dat kragopwekkers is baie nuttig in die skoonmaak van jou kode en deur gee jou 'n baie unieke manier om omsluit en modularize kode. In 'n situasie waar jy iets nodig het om voortdurend spoeg uit waardes op grond van sy eie interne verwerking en toe dat daar iets moet genoem word vanaf enige plek in jou kode (en nie net binne 'n lus of 'n blok byvoorbeeld), kragopwekkers is die kenmerk te gebruik.

'n Uittreksel voorbeeld sou 'n Fibonacci getal kragopwekker wat nie binne 'n lus nie leef nie en wanneer dit genoem word vanaf enige plek sal altyd die volgende getal in die reeks terugkeer:

def fib():
    first = 0
    second = 1
    yield first
    yield second

    while 1:
        next = first + second
        yield next
        first = second
        second = next

fibgen1 = fib()
fibgen2 = fib()

Nou het jy twee Fibonacci-getal kragopwekker voorwerpe wat jy kan bel vanaf enige plek in jou kode en hulle sal altyd steeds groter Fibonacci getalle terugkeer in volgorde soos volg:

>>> fibgen1.next(); fibgen1.next(); fibgen1.next(); fibgen1.next()
0
1
1
2
>>> fibgen2.next(); fibgen2.next()
0
1
>>> fibgen1.next(); fibgen1.next()
3
5

Die pragtige ding oor kragopwekkers is dat hulle staat omsluit sonder om te gaan deur die hoepels van die skep van voorwerpe. Een manier om te dink oor hulle is as "funksies" wat hul interne toestand onthou.

Ek het die Fibonacci voorbeeld van Python Generators - Wat is hulle? en met 'n bietjie verbeelding, kan jy kom met 'n baie ander situasies waar kragopwekkers maak vir 'n groot alternatief vir loops en ander tradisionele iterasie konstrukte for.

Die eenvoudige verduideliking: Oorweeg 'n for verklaring

for item in iterable:
   do_stuff()

'n groot deel van die tyd, al die items in iterable hoef nie daar te wees van die begin af, maar kan gegenereer word op die vlieg as hulle nodig is. Dit kan 'n baie meer doeltreffende in beide

  • ruimte (wat jy nodig het om nooit al die items gelyktydig te stoor) en
  • tyd (die iterasie kan klaar te maak voordat al die items wat nodig is).

Ander keer, het jy nie eens weet al die items voor die tyd. Byvoorbeeld:

for command in user_input():
   do_stuff_with(command)

Jy het geen manier om te weet al die gebooie van die gebruiker se vooraf, maar jy kan 'n lekker lus soos hierdie gebruik as jy 'n kragopwekker te oorhandig jy beveel:

def user_input():
    while True:
        wait_for_command()
        cmd = get_command()
        yield cmd

Met kragopwekkers kan jy ook iterasie oor oneindige rye, wat natuurlik nie moontlik wanneer iterating oor houers.

My gunsteling gebruike is "filter" en "verminder" bedrywighede.

Kom ons sê ons is 'n lêer te lees, en net wil die lyne wat begin met "##".

def filter2sharps( aSequence ):
    for l in aSequence:
        if l.startswith("##"):
            yield l

Ons kan dan gebruik maak van die kragopwekker funksie in 'n behoorlike lus

source= file( ... )
for line in filter2sharps( source.readlines() ):
    print line
source.close()

Die verminder voorbeeld is soortgelyk. Kom ons sê ons het 'n lêer waar ons nodig het om blokke van <Location>...</Location> lyne op te spoor. [Nie tags HTML, maar lyne wat gebeur tag-agtige kyk.]

def reduceLocation( aSequence ):
    keep= False
    block= None
    for line in aSequence:
        if line.startswith("</Location"):
            block.append( line )
            yield block
            block= None
            keep= False
        elif line.startsWith("<Location"):
            block= [ line ]
            keep= True
        elif keep:
            block.append( line )
        else:
            pass
    if block is not None:
        yield block # A partial block, icky

Weereens, kan ons hierdie kragopwekker gebruik in 'n behoorlike vir lus.

source = file( ... )
for b in reduceLocation( source.readlines() ):
    print b
source.close()

Die idee is dat 'n kragopwekker funksie stel ons in staat om te filtreer of te verminder 'n ry, die vervaardiging van 'n ander volgorde een waarde op 'n slag.

'n praktiese voorbeeld waar jy gebruik maak van 'n kragopwekker kan maak is as jy 'n soort van vorm en jy wil Itereer oor sy hoeke, kante of wat ook al. Vir my eie projek (source code hier ) het ek 'n reghoek:

class Rect():

    def __init__(self, x, y, width, height):
        self.l_top  = (x, y)
        self.r_top  = (x+width, y)
        self.r_bot  = (x+width, y+height)
        self.l_bot  = (x, y+height)

    def __iter__(self):
        yield self.l_top
        yield self.r_top
        yield self.r_bot
        yield self.l_bot

Nou kan ek 'n reghoek en lus te skep oor sy hoeke:

myrect=Rect(50, 50, 100, 100)
for corner in myrect:
    print(corner)

In plaas van __iter__ jy kan 'n metode iter_corners het en noem dat met for corner in myrect.iter_corners(). Dis net meer elegant te __iter__ gebruik sedertdien het ons kan die klasnaam byvoorbeeld direk in die for uitdrukking gebruik.

In beginsel vermy call-back funksies toe iterating oor insette handhawing staat.

hier en hier vir 'n oorsig van wat gedoen kan word met behulp van kragopwekkers.

Sommige goeie antwoorde hier, maar ek wil ook beveel 'n volledige lees van die Python funksionele programmering handleiding wat help verduidelik sommige van die meer kragtige gebruik-gevalle van kragopwekkers.

Ek gebruik kragopwekkers wanneer ons web bediener optree as 'n gevolmagtigde:

  1. Die kliënt versoek 'n proxy url van die bediener
  2. Die bediener begin om die teiken url laai
  3. Die bediener opbrengste om die resultate terug te keer na die kliënt so gou as dit hulle kry

Sedert die stuur metode van 'n kragopwekker is nie genoem, hier is 'n voorbeeld:

def test():
    for i in xrange(5):
        val = yield
        print(val)

t = test()

# Proceed to 'yield' statement
next(t)

# Send value to yield
t.send(1)
t.send('2')
t.send([3])

Dit wys die moontlikheid om 'n waarde te stuur na 'n lopende kragopwekker. 'N Meer gevorderde kursus oor kragopwekkers in die video hieronder (insluitend yield van explination, kragopwekkers vir parallelle verwerking, ontsnap die rekursie perk, ens.)

David Beazley op kragopwekkers by PyCon 2014

Die drie dinge. Enige tyd wat jy wil 'n reeks van items te genereer, maar wil nie in 'n keer na 'Materialise' hulle almal in 'n lys te hê. Byvoorbeeld, kan jy 'n eenvoudige kragopwekker wat priemgetalle terugkeer hê:

def primes():
    primes_found = set()
    primes_found.add(2)
    yield 2
    for i in itertools.count(1):
        candidate = i * 2 + 1
        if not all(candidate % prime for prime in primes_found):
            primes_found.add(candidate)
            yield candidate

Jy kan dan gebruik wat aan die produkte van die daaropvolgende primes te genereer:

def prime_products():
    primeiter = primes()
    prev = primeiter.next()
    for prime in primeiter:
        yield prime * prev
        prev = prime

Dit is redelik triviaal voorbeelde, maar jy kan sien hoe dit nuttig vir die verwerking van groot (potensieel oneindige!) Datastelle sonder dat hulle genereer in advance, wat slegs een van die meer voor die hand liggend gebruike kan wees.

Ook goed vir die druk van die priemgetalle tot N:

def genprime(n=10):
    for num in range(3, n+1):
        for factor in range(2, num):
            if num%factor == 0:
                break
        else:
            yield(num)

for prime_num in genprime(100):
    print(prime_num)
Gelisensieer onder: CC-BY-SA met toeskrywing
Nie verbonde aan StackOverflow
scroll top