Generator Expressions vs. List Forståelse

stemmer
334

Når bør du bruke generator uttrykk og når bør du bruke liste oppfattelser i Python?

# Generator expression
(x*2 for x in range(256))

# List comprehension
[x*2 for x in range(256)]
Publisert på 06/09/2008 klokken 20:07
kilden bruker
På andre språk...                            


9 svar

stemmer
230

John svar er bra (som liste oppfattelser er bedre når du ønsker å iterere over noe flere ganger). Men det er også verdt å merke seg at du bør bruke en liste hvis du vil bruke noen av liste metoder. For eksempel vil følgende kode ikke:

def gen():
    return (something for something in get_some_stuff())

print gen()[:2]     # generators don't support indexing or slicing
print [5,6] + gen() # generators can't be added to lists

I utgangspunktet bruker en generator uttrykk hvis alt du gjør er itera gang. Hvis du ønsker å lagre og bruke de genererte resultater, da er du sannsynligvis bedre med en liste forståelse.

Siden ytelsen er den vanligste grunnen til å velge en over den andre, er mitt råd å ikke bry deg om det, og bare plukke en; hvis du finner ut at programmet kjører for sakte, da og bare da bør du gå tilbake og bekymre tuning koden din.

Svarte 06/09/2008 kl. 20:54
kilden bruker

stemmer
143

Gjentar over generator uttrykk eller liste forståelse vil gjøre det samme. Men listen forståelse vil skape hele listen i minnet første mens generatoren uttrykket vil skape elementene på fly, slik at du kan bruke det for svært store (og også uendelige!) Sekvenser.

Svarte 06/09/2008 kl. 20:11
kilden bruker

stemmer
78

Bruk liste oppfattelser når resultatet må iterated over flere ganger, eller hvor hastighet er viktig. Bruk generator uttrykk hvor det er dermed stort eller uendelig.

Svarte 06/09/2008 kl. 20:10
kilden bruker

stemmer
47

Det viktige poenget er at listen forståelse skaper en ny liste. Generatoren danner et et iterable objekt som vil "filter" kildematerialet on-the-fly som man forbruker biter.

Tenk deg at du har en 2TB loggfil kalt "hugefile.txt", og du vil at innholdet og lengden for alle linjer som begynner med ordet "ENTRY".

Så du prøver å starte opp med å skrive en liste forståelse:

logfile = open("hugefile.txt","r")
entry_lines = [(line,len(line)) for line in logfile if line.startswith("ENTRY")]

Dette slurper i seg hele filen, behandler hver linje, og lagrer de samsvarende linjene i matrisen. Denne rekken kan derfor inneholde opptil 2 TB med innhold. Det er mye RAM, og sannsynligvis ikke praktisk for ditt formål.

Så i stedet kan vi bruke en generator for å bruke et "filter" til vårt innhold. Ingen data er faktisk lest før vi starter gjentar over resultatet.

logfile = open("hugefile.txt","r")
entry_lines = ((line,len(line)) for line in logfile if line.startswith("ENTRY"))

Ikke engang en eneste linje er blitt lest fra vår fil ennå. Faktisk si at vi ønsker å filtrere resultatet vårt enda lenger:

long_entries = ((line,length) for (line,length) in entry_lines if length > 80)

Fortsatt ingenting har blitt lest, men vi har angitt nå to generatorer som virker på våre data som vi ønsker.

Lar skrive ut våre filtrert linjer til en annen fil:

outfile = open("filtered.txt","a")
for entry,length in long_entries:
    outfile.write(entry)

leser vi inndatafilen. Som vår forsløyfen fortsetter å be om ytterligere linjer, den long_entrieskrever generatoren linjer fra entry_linesgeneratoren, tilbake bare de hvis lengde er større enn 80 tegn. Og i sin tur, entry_linesber generator linjer (filtrerte som angitt) fra logfileiterator, som igjen leser filen.

Så i stedet for "presser" data til utgangsfunksjon i form av et fullt befolket liste, du gir utgangsfunksjonen en måte å "trekke" data kun når det er nødvendig. Dette er i vårt tilfelle mye mer effektiv, men ikke fullt så fleksibel. Generatorer er én måte, hver gjennomstrømning; data fra loggfilen vi har lest blir umiddelbart forkastet, så vi kan ikke gå tilbake til en tidligere linje. På den annen side, trenger vi ikke å bekymre deg for å holde data rundt når vi er ferdige med det.

Svarte 04/04/2014 kl. 09:14
kilden bruker

stemmer
41

Fordelen med en generator uttrykket er at den bruker mindre minne siden det ikke bygger hele listen samtidig. Generator uttrykk er best brukt når listen er en mellommann, som for eksempel å summere resultatene, eller lage en dict ut av resultatene.

For eksempel:

sum(x*2 for x in xrange(256))

dict( ((k, some_func(k) for k in some_list_of_keys) )

Fordelen er at listen ikke er fullstendig generert, og således lite minne benyttes (og bør også være raskere)

Du bør imidlertid bruke liste oppfattelser når den ønskede endelige produktet er en liste. Du kommer ikke til å spare noen memeory bruke generatorer uttrykk, siden du vil ha den genererte listen. Du får også fordelen av å kunne bruke noen av listefunksjonene som sorteres eller reversert.

For eksempel:

reversed( [x*2 for x in xrange(256)] )
Svarte 10/10/2008 kl. 01:42
kilden bruker

stemmer
9

Ved oppretting av en generator av et foranderlig objekt (som en liste) være oppmerksom på at generatoren vil bli evaluert på tilstanden av listen på tidspunktet for bruk av aggregatet, ikke på tidspunktet for dannelsen av generatoren:

>>> mylist = ["a", "b", "c"]
>>> gen = (elem + "1" for elem in mylist)
>>> mylist.clear()
>>> for x in gen: print (x)
# nothing

Hvis det er noen sjanse på listen bli endret (eller et foranderlig objekt i den listen), men du trenger staten ved opprettelsen av generatoren må du bruke en liste forståelse i stedet.

Svarte 12/03/2016 kl. 22:21
kilden bruker

stemmer
5

Jeg bruker Hadoop fruktfyll modulen . Jeg tror dette er et godt eksempel å ta et notat av:

import mincemeat

def mapfn(k,v):
    for w in v:
        yield 'sum',w
        #yield 'count',1


def reducefn(k,v): 
    r1=sum(v)
    r2=len(v)
    print r2
    m=r1/r2
    std=0
    for i in range(r2):
       std+=pow(abs(v[i]-m),2)  
    res=pow((std/r2),0.5)
    return r1,r2,res

Her generatoren får tall ut av en tekstfil (så stor som 15 GB) og gjelder enkel matematikk på disse tallene som bruker Hadoop er kart redusere. Hvis jeg ikke hadde brukt flytefunksjonen, men i stedet en liste forståelse, ville det ha tatt mye lengre tid å beregne summene og gjennomsnittlig (og ikke minst plass kompleksitet).

Hadoop er et godt eksempel for å bruke alle fordelene av generatorer.

Svarte 04/01/2016 kl. 20:31
kilden bruker

stemmer
3

Noen ganger kan du komme unna med tee funksjon fra itertools , returnerer flere iteratorer for samme generator som kan brukes uavhengig av hverandre.

Svarte 10/09/2008 kl. 00:58
kilden bruker

stemmer
0

hva med bruk [(exp for x i ITER)] for å få det beste for begge. Resultater fra generator forståelse samt liste metoder

Svarte 16/03/2019 kl. 07:57
kilden bruker

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more