Frage Ist es Pythonic, Listenkompromittierungen für nur Nebenwirkungen zu verwenden?


Denken Sie an eine Funktion, die ich für ihre Nebenwirkungen, keine Rückgabewerte (wie Drucken auf dem Bildschirm, Aktualisieren der GUI, Drucken in eine Datei, etc.) aufrufen.

def fun_with_side_effects(x):
    ...side effects...
    return y

Jetzt ist es Pythonisch Listencomprehensions verwenden, um diese Funktion aufzurufen:

[fun_with_side_effects(x) for x in y if (...conditions...)]

Beachten Sie, dass ich die Liste nirgendwo speichern kann

Oder soll ich das Func so nennen:

for x in y:
    if (...conditions...):
        fun_with_side_effects(x)

Was ist besser und warum?


75
2018-04-22 08:22


Ursprung


Antworten:


Es ist sehr antipythonisch, und jeder erfahrene Pythonist wird dir die Hölle heiß machen. Die Zwischenliste wird nach der Erstellung verworfen und könnte möglicherweise sehr, sehr groß und daher teuer in der Erstellung sein.


61
2018-04-22 08:24



Sie sollten kein a verwenden Liste Verständnis, denn wie die Leute gesagt haben, wird das eine große temporäre Liste aufbauen, die du nicht brauchst. Die folgenden zwei Methoden sind äquivalent:

consume(side_effects(x) for x in xs)

for x in xs:
    side_effects(x)

mit der Definition von consume von dem itertools man seite:

def consume(iterator, n=None):
    "Advance the iterator n-steps ahead. If n is none, consume entirely."
    # Use functions that consume iterators at C speed.
    if n is None:
        # feed the entire iterator into a zero-length deque
        collections.deque(iterator, maxlen=0)
    else:
        # advance to the empty slice starting at position n
        next(islice(iterator, n, n), None)

Letzteres ist natürlich klarer und leichter zu verstehen.


27
2018-04-22 08:38



List Comprehensions dienen zum Erstellen von Listen. Und wenn Sie nicht gerade eine Liste erstellen, sollten Sie das tun nicht Verwenden Sie List Comprehensions.

Also würde ich für die zweite Option, nur über die Liste iterieren und dann die Funktion aufrufen, wenn die Bedingungen gelten.


20
2018-04-22 08:27



Zweitens ist besser.

Denken Sie an die Person, die Ihren Code verstehen müsste. Du kannst schlechtes Karma leicht mit dem ersten bekommen :)

Sie können mit filter () in die Mitte der beiden gehen. Betrachten Sie das Beispiel:

y=[1,2,3,4,5,6]
def func(x):
    print "call with %r"%x

for x in filter(lambda x: x>3, y):
    func(x)

9
2018-04-22 08:30



Hängt von deinem Ziel ab.

Wenn Sie versuchen, eine Operation für jedes Objekt in einer Liste durchzuführen, sollte der zweite Ansatz übernommen werden.

Wenn Sie versuchen, eine Liste aus einer anderen Liste zu generieren, können Sie Listenverstehen verwenden.

Explizit ist besser als implizit.   Einfach ist besser als komplex. (Python Zen)


2
2018-04-22 08:30



Du kannst tun

for z in (fun_with_side_effects(x) for x in y if (...conditions...)): pass

aber es ist nicht sehr hübsch.


1
2018-04-22 08:33



Ich würde das stattdessen tun:

any(fun_with_side_effects(x) and False for x in y if (...conditions...))

Dies ist ein Generatorausdruck und generiert keine zufällige Liste, die ausgegeben wird. Ich denke, es ist hässlich und ich würde es nicht wirklich im Code machen. Aber wenn Sie darauf bestehen, Ihre Loops auf diese Weise zu implementieren, würde ich es so machen.

Ich habe das Gefühl, dass die Listen-Comprehensions und ihr Format den Versuch signalisieren sollten, etwas zu verwenden, das zumindest einem funktionalen Stil ähnelt. Dinge mit Nebeneffekten zu setzen, die diese Annahme brechen, führt dazu, dass die Leute Ihren Code sorgfältiger lesen müssen, und ich denke, das ist eine schlechte Sache.


0
2018-04-22 08:38