Wat kan jy gebruik Python generator funksies vir?
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.
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 yield
ing 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.
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.
- Veral interessant is dat dit nou moontlik om werk die opbrengs veranderlike van buite die kragopwekker funksie , dus maak dit moontlik om dinamiese en verweef coroutines met relatief min moeite te skep.
- Sien ook PEP 342: Coroutines via Verbeterde Generators vir meer inligting .
Ek gebruik kragopwekkers wanneer ons web bediener optree as 'n gevolmagtigde:
- Die kliënt versoek 'n proxy url van die bediener
- Die bediener begin om die teiken url laai
- 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.)
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)