Kiedy nie jest dobry czas na korzystanie z generatorów Pythona?

Jest to raczej odwrotność do czego można używać funkcji generatora Pythona?: Generatory Pythona, wyrażenia generatora i moduł itertools to niektóre z moich ulubionych funkcji Pythona w dzisiejszych czasach. Są one szczególnie przydatne podczas konfigurowania łańcuchów operacji do wykonywania na dużej stercie danych - często używam ich podczas przetwarzania plików DSV.

Kiedy więc Nie jest dobry czas na użycie generatora, wyrażenia generatora, czy itertools funkcja?

  • Kiedy powinienem preferować zip() nad itertools.izip(), czy
  • range() over xrange(), or
  • [x for x in foo] over (x for x in foo)?

Oczywiście, w końcu musimy "rozwiązać" generator w rzeczywiste dane, Zwykle poprzez utworzenie listy lub iterację nad nią za pomocą pętli nie-generatora. Czasami po prostu musimy znać długość. Nie o to proszę.

Używamy generatorów, aby nie przypisywać nowych list do pamięci dla tymczasowych danych. To szczególnie ma to sens w przypadku dużych zbiorów danych. Czy ma to sens również dla małych zbiorów danych? Czy istnieje zauważalna wymiana pamięci / procesora?

Jestem szczególnie zainteresowany, jeśli ktoś zrobił jakieś profilowanie na ten temat, w świetle otwierającej Oczy dyskusji list comprehension performance vs. map () i filter () . (alt link)

Author: Community, 2008-10-29

9 answers

Użyj listy zamiast generatora, gdy:

1) Musisz uzyskać dostęp do danych wiele razy (tzn. buforować wyniki zamiast je przekomputowywać):

for i in outer:           # used once, okay to be a generator or return a list
    for j in inner:       # used multiple times, reusing a list is better
         ...

2) Potrzebujesz dostępu losowego (lub dowolnego dostępu innego niż kolejność Sekwencyjna forward):

for i in reversed(data): ...     # generators aren't reversible

s[i], s[j] = s[j], s[i]          # generators aren't indexable

3) Musisz dołączyć ciągi znaków (które wymagają dwóch przejść nad danymi):

s = ''.join(data)                # lists are faster than generators in this use case

4) używasz PyPy które czasami nie mogą zoptymalizować kodu generatora tak bardzo, jak to możliwe z normalnymi wywołaniami funkcji i manipulacjami list.

 44
Author: Raymond Hettinger,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-10-30 05:59:33

Ogólnie rzecz biorąc, nie używaj generatora, gdy potrzebujesz operacji na liście, takich jak len (), reversed () i tak dalej.

Mogą być również sytuacje, w których nie chcesz leniwej oceny (np. wykonać wszystkie obliczenia z góry, aby móc zwolnić zasób). W takim przypadku wyrażenie listy może być lepsze.

 38
Author: Ryan Ginstrom,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-29 04:42:05

Profil, Profil, Profil.

Profilowanie kodu jest jedynym sposobem, aby wiedzieć, czy to, co robisz, ma jakikolwiek wpływ.

Większość zastosowań xrange, Generatory, itp są ponad statyczne wielkości, małe zbiory danych. Dopiero gdy dotrzesz do dużych zbiorów danych, to naprawdę robi różnicę. range () vs. xrange() to głównie kwestia uczynienia kodu trochę brzydszym i nie utraty niczego, a może zyskania czegoś.

Profil, Profil, Profil.

 23
Author: Jerub,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-29 11:37:31

You should never favor zip over izip, range over xrange, lub list comprehensions over generator comprehensions. W Pythonie 3.0 range ma xrange-Jak semantyka i zip mA izip-Jak semantyka.

Składanie List jest w rzeczywistości bardziej przejrzyste, jak list(frob(x) for x in foo) w tych czasach, gdy potrzebujesz rzeczywistej listy.

 17
Author: Steven Huwig,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-06 19:12:51

Jeśli chodzi o wydajność: jeśli używasz psyco, listy mogą być nieco szybsze niż Generatory. W poniższym przykładzie listy są o prawie 50% szybsze podczas korzystania z psyco.full ()

import psyco
import time
import cStringIO

def time_func(func):
    """The amount of time it requires func to run"""
    start = time.clock()
    func()
    return time.clock() - start

def fizzbuzz(num):
    """That algorithm we all know and love"""
    if not num % 3 and not num % 5:
        return "%d fizz buzz" % num
    elif not num % 3:
        return "%d fizz" % num
    elif not num % 5:
        return "%d buzz" % num
    return None

def with_list(num):
    """Try getting fizzbuzz with a list comprehension and range"""
    out = cStringIO.StringIO()
    for fibby in [fizzbuzz(x) for x in range(1, num) if fizzbuzz(x)]:
        print >> out, fibby
    return out.getvalue()

def with_genx(num):
    """Try getting fizzbuzz with generator expression and xrange"""
    out = cStringIO.StringIO()
    for fibby in (fizzbuzz(x) for x in xrange(1, num) if fizzbuzz(x)):
        print >> out, fibby
    return out.getvalue()

def main():
    """
    Test speed of generator expressions versus list comprehensions,
    with and without psyco.
    """

    #our variables
    nums = [10000, 100000]
    funcs = [with_list, with_genx]

    #  try without psyco 1st
    print "without psyco"
    for num in nums:
        print "  number:", num
        for func in funcs:
            print func.__name__, time_func(lambda : func(num)), "seconds"
        print

    #  now with psyco
    print "with psyco"
    psyco.full()
    for num in nums:
        print "  number:", num
        for func in funcs:
            print func.__name__, time_func(lambda : func(num)), "seconds"
        print

if __name__ == "__main__":
    main()

Wyniki:

without psyco
  number: 10000
with_list 0.0519102208309 seconds
with_genx 0.0535933367509 seconds

  number: 100000
with_list 0.542204280744 seconds
with_genx 0.557837353115 seconds

with psyco
  number: 10000
with_list 0.0286369007033 seconds
with_genx 0.0513424889137 seconds

  number: 100000
with_list 0.335414877839 seconds
with_genx 0.580363490491 seconds
 6
Author: Ryan Ginstrom,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-30 14:38:40

Jak wspominasz, "to szczególnie ma sens dla dużych zbiorów danych", myślę, że to odpowiada na twoje pytanie.

Jeśli nie trafiasz żadnych ścian, pod względem wydajności, nadal możesz trzymać się list i standardowych funkcji. Następnie, gdy napotkasz problemy z wydajnością, dokonaj przełącznika.

Jak wspomniał @u0b34a0f6ae w komentarzach, jednak korzystanie z generatorów na początku może ułatwić skalowanie do większych zbiorów danych.

 6
Author: monkut,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-26 00:19:05

Powinieneś preferować składanie list, jeśli chcesz zachować wartości dla czegoś innego później, a rozmiar Twojego zestawu nie jest zbyt duży.

Na przykład: tworzysz listę, którą będziesz pętlować kilka razy później w swoim programie.

Do pewnego stopnia można myśleć o generatorach jako zastępstwie iteracji (pętli) vs.list comprehensions jako rodzaju inicjalizacji struktury danych. Jeśli chcesz zachować strukturę danych, użyj list compenensions.

 3
Author: minty,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-12-31 16:21:02

Jeśli chodzi o wydajność, nie przychodzi mi do głowy, że chciałbyś użyć listy zamiast generatora.

 2
Author: Jason Baker,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-29 11:44:06

Nigdy nie znalazłem sytuacji, w której Generatory utrudniałyby to, co próbujesz zrobić. Istnieje jednak wiele przypadków, w których korzystanie z generatorów nie pomogłoby Ci bardziej niż nie Korzystanie z nich.

Na przykład:

sorted(xrange(5))

Nie oferuje żadnej poprawy w stosunku do:

sorted(range(5))
 2
Author: Jeremy Cantrell,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-29 16:44:36