Frage

Das ist eher das Gegenteil von Was können Sie Python-Generator-Funktionen verwenden für : python-Generatoren, Generator Ausdrücke und das itertools Modul sind einige meiner Lieblings-Features von python in diesen Tagen. Sie sind besonders nützlich, wenn Ketten von Operationen Einrichten auf einem großen Haufen von Daten durchzuführen - ich sie häufig verwenden, wenn DSV-Dateien verarbeiten

.

So, wenn es nicht einen guten Zeitpunkt, einen Generator oder einen Generator Ausdruck oder eine itertools Funktion zu benutzen?

  • Wann sollte ich lieber zip() über itertools.izip() oder
  • range() über xrange() oder
  • [x for x in foo] über (x for x in foo)?

Natürlich müssen wir schließlich zu „lösen“ einem Generator in tatsächliche Daten, in der Regel eine Liste erstellen oder mit einer nicht-Generator Schleife über sie iterieren. Manchmal müssen wir nur noch die Länge kennen. Das ist nicht, was ich frage.

Wir verwenden Generatoren, so dass wir nicht neue Listen in den Speicher für die Zwischendaten zuweisen. Dies vor allem sinnvoll für große Datensätze. Ist es auch Sinn für kleine Datenmengen machen? Gibt es einen merklichen Speicher / CPU-trade-off?

Ich bin besonders interessiert, wenn jemand etwas Profilierung auf diesem, angesichts der augenöffnende Diskussion von Liste Verständnis Leistung vs. map () und filter () . ( alt Link )

War es hilfreich?

Lösung

Verwenden Sie eine Liste anstelle eines Generators, wenn:

1) Sie müssen die Daten für den Zugriff auf mehr mal (das heißt Cache die Ergebnisse, anstatt sie neu zu berechnen):

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) Sie müssen random access (oder ein Zugriff auf andere als Vorwärts-Reihenfolge):

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

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

3) Sie müssen beitreten Saiten (die zwei Durchgänge über die Daten benötigt):

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

4) Sie verwenden PyPy , die manchmal nicht Generator Code so viel optimieren kann, wie es kann mit normalen Funktionsaufrufen und Listen Manipulationen.

Andere Tipps

In der Regel keinen Generator verwenden, wenn Sie Listenoperationen benötigen, wie len (), umgekehrt (), und so weiter.

Es kann auch mal sein kann, wenn Sie faul Auswertung nicht wollen (zum Beispiel alle die Berechnung nach vorne zu tun, damit Sie eine Ressource freigeben können). In diesem Fall könnte eine Liste Ausdruck besser sein.

Profil, Profil, Profil.

Ihr Code Profilierungs ist der einzige Weg zu wissen, ob das, was Sie tun, überhaupt keine Wirkung hat.

Die meisten Verwendungen von xrange, Generatoren, etc. sind über statische Größe, kleine Datenmengen. Es ist nur, wenn Sie auf große Datenmengen erhalten, die es wirklich einen Unterschied macht. range () vs. xrange () ist meist nur eine Frage der Herstellung der Code ein ganz klein wenig mehr hässlich aussehen, und nichts zu verlieren, und vielleicht etwas zu gewinnen.

Profil, Profil, Profil.

Sie sollten nie bevorzugen zip über izip , range über xrange oder Listenkomprehensionen über Generator Comprehensions. In Python 3.0 range hat xrange-ähnliche Semantik und zip hat izip-ähnliche Semantik.

Liste Comprehensions sind eigentlich klarer wie list(frob(x) for x in foo) für jene Zeiten, die Sie eine aktuelle Liste benötigen.

Wie Sie erwähnen: „Das ist besonders sinnvoll für große Datenmengen“, ich denke, das Ihre Frage beantwortet.

Wenn Ihr keine Wände schlagen, Performance-weise, können Sie auf Listen und Standardfunktionen bleiben nach wie vor. Dann, wenn Sie Probleme mit Leistung führen Sie den Schalter machen.

Wie u0b34a0f6ae in den Kommentaren von @ erwähnt, jedoch kann mit Generatoren zu Beginn macht es einfacher für Sie, um größere Datenmengen zu skalieren.

Leistung in Bezug auf: Wenn psyco verwenden, können Listen sein ziemlich viel schneller als Generatoren. Im Beispiel unten, Listen sind fast 50% schneller, wenn psyco.full mit ()

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

Ergebnisse:

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

Was die Leistung betrifft, so kann ich mich nicht von irgendwelchen Zeiten, dass Sie eine Liste über einen Generator verwenden mögen.

Ich habe nie eine Situation, wo Generatoren behindern würden, was Sie zu tun versuchen. Es gibt jedoch viele Fälle, in denen mit Generatoren würden Sie nicht mehr helfen, jede als sie nicht verwenden.

Zum Beispiel:

sorted(xrange(5))

bietet keine Verbesserung gegenüber:

sorted(range(5))

Sie sollten Listenkomprehensionen es vorziehen, wenn Sie die Werte für etwas um zu halten brauchen, sonst später und die Größe Ihres Satzes nicht zu groß ist.

Zum Beispiel:  Sie erstellen eine Liste, die Sie Schleife über mehrere Male später in Ihrem Programm.

In gewissem Maß Sie von Generatoren als Ersatz für Iteration (Loops) vs. Listenkomprehensionen als eine Art von Datenstruktur Initialisierung denken können. Wenn Sie die Datenstruktur halten wollen, dann Listenkomprehensionen verwenden.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top