2016-08-16 1 views
3

Ich liebe Python. Eine Sache, die mich ein wenig nervt, ist jedoch, dass ich nicht weiß, wie man funktionale Aktivitäten auf eine fließende Weise wie eine Dose in Javascript formatiert.Python flüssiger Filter, Karte, etc.

Beispiel (zufällig vor Ort erstellt): Können Sie mir helfen, dies fließend in Python zu konvertieren?

var even_set = [1,2,3,4,5] 
.filter(function(x){return x%2 === 0;}) 
.map(function(x){ 
    console.log(x); // prints it for fun 
    return x; 
}) 
.reduce(function(num_set, val) { 
    num_set[val] = true; 
}, {}); 

Ich würde gerne wissen, ob es flüssige Optionen gibt? Vielleicht eine Bibliothek.

Im Allgemeinen, ich habe für die meisten Dinge mit Listenkomprehensionen aber es ist ein echtes Problem, wenn ich

zB ausdrucken möchten, Wie kann ich jede gerade Zahl zwischen 1 drucken - 5 in Python 2.x Listenverständnis (Python 3 print() als Funktion aber Python 2 nicht). Es ist auch ein bisschen nervig, dass eine Liste erstellt und zurückgegeben wird. Ich würde lieber nur für die Schleife.

+0

Verwenden Sie einfach eine Schleife. Die funktionale Notation sendet ein starkes Signal, dass Sie etwas ohne Nebenwirkungen tun, ein Signal, das Sie nicht für einen Vorgang wie Drucken senden möchten. – user2357112

+0

Die Antwort ist __nicht ein Verständnis zum Ausdrucken verwenden. Verständnis ist ein funktionales Konstrukt, und wenn Menschen ein Verständnis sehen, erwarten sie keine Nebenwirkung. –

+0

Sie haben Probleme, weil der von Ihnen bereitgestellte Beispielcode alle nicht-funktionalen Dinge erledigt – naomik

Antwort

1

Generatoren, Iteratoren und itertools verleihen den Verkettungs- und Filteraktionen zusätzliche Möglichkeiten. Aber anstatt sich selten genutzte Dinge zu merken (oder nachzuschlagen), tendiere ich zu Hilfsfunktionen und -verständnissen.

Zum Beispiel in diesem Fall übernimmt die Protokollierung mit einer Hilfsfunktion:

def echo(x): 
    print(x) 
    return x 

Auswahl sogar Werte mit der if Klausel eines Verständnis einfach ist. Und da die endgültige Ausgabe ein Wörterbuch ist, verwenden Sie diese Art von Verständnis:

In [118]: d={echo(x):True for x in s if x%2==0} 
2 
4 

In [119]: d 
Out[119]: {2: True, 4: True} 

oder diese Werte zu einem vorhandenen Wörterbuch hinzuzufügen, verwenden Sie update.

new_set.update({echo(x):True for x in s if x%2==0}) 

andere Möglichkeit, dies zu schreiben, ist mit einem Zwischen Generator:

{y:True for y in (echo(x) for x in s if x%2==0)} 

oder das Echo und Filter in einem Generator

def even(s): 
    for x in s: 
     if x%2==0: 
      print(x) 
      yield(x) 

von einem dict comp gefolgt es unter Verwendung kombiniert werden:

{y:True for y in even(s)} 
+0

obwohl nicht gerade eine fließende Schnittstelle. Dies scheint die klarste verfügbare Option zu sein. – ThinkBonobo

+0

Python Anleitung zur funktionalen Programmierung https://docs.python.org/2/howto/functional.html – hpaulj

1

Der größte Dealbreaker für den von Ihnen geschriebenen Code ist, dass Python keine anonymen Mehrzeilenfunktionen unterstützt. Der Rückgabewert von filter oder map ist eine Liste, so dass Sie sie weiterhin verketten können, wenn Sie dies wünschen. Sie müssen jedoch entweder die Funktionen im Voraus definieren oder ein Lambda verwenden.

+1

Single-Expression-Lambdas sind ein gutes Geschäft. Ich bin froh, dass sie den OP-Code nicht unterstützen. +1 – naomik

2

Argumente gegen dies ungeachtet, hier ist eine Übersetzung in Python von Ihrem JS-Code.

from __future__ import print_function 
from functools import reduce 

def print_and_return(x): 
    print(x) 
    return x 

def isodd(x): 
    return x % 2 == 0 

def add_to_dict(d, x): 
    d[x] = True 
    return d 

even_set = list(reduce(add_to_dict, 
       map(print_and_return, 
       filter(isodd, [1, 2, 3, 4, 5])), {})) 

Es sollte 2 und Python auf beiden Python arbeiten 3.

+0

Ich denke, am Ende des Tages hatte ich auf eine fließende Art zu tun, was du in der letzten Zeile getan hast. sogar den Druck überspringen und einen Teil davon zurückgeben. es wäre schön, wenn es etwas wie ".filter (...). reduce (...)" wäre, das sagte, wenn es nicht was Python ist, ist es nicht was es tut. Ich war nur neugierig – ThinkBonobo

1

Comprehensions sind die fließend python Art und Weise Filter/Karte Operationen der Handhabung.

Code wäre so etwas wie:

def evenize(input_list): 
    return [x for x in input_list if x % 2 == 0] 

Comprehensions nicht gut mit Nebenwirkungen wie Konsolenprotokollierung arbeiten, so tun, dass in einer separaten Schleife. Verkettung von Funktionsaufrufen ist nicht wirklich das übliche Idiom in Python. Erwarten Sie nicht, dass das hier Ihr Brot und Butter ist. Python-Bibliotheken neigen dazu, dem Muster "alter state" oder "return a value, but not both" zu folgen. Einige Ausnahmen existieren.

Edit: Auf der Plusseite, bietet Python mehrere Aromen von Comprehensions, die genial sind:

Liste Verständnis: [x for x in range(3)] == [0, 1, 2]

Set Verständnis: {x for x in range(3)} == {0, 1, 2}

Dict Verständnis: `{x : x ** 2 für x im Bereich (3)} == {0: 0, 1: 1, 2: 4}

Generatorverständnis (oder Generatorausdruck): (x for x in range(3)) == <generator object <genexpr> at 0x10fc7dfa0>

Mit dem Verständnis des Generators wurde noch nichts ausgewertet, so dass es eine großartige Möglichkeit ist, Speicherauslastung beim Pipelining von großen Sammlungen zu vermeiden.

Zum Beispiel, wenn Sie versuchen, die folgenden zu tun, auch mit python3 Semantik für range:

for number in [x**2 for x in range(10000000000000000)]: 
    print(number) 

Sie einen Speicherfehler erhalten wird versucht, die erste Liste zu bauen. Auf der anderen Seite, ändern Sie die Liste Verständnis in einen Generator Verständnis:

for number in (x**2 for x in range(1e20)): 
    print(number) 

und es gibt kein Speicherproblem (es dauert nur ewig zu laufen). Was passiert, ist, dass das Bereichsobjekt aufgebaut wird (das nur die Start-, Stopp- und Schrittwerte (0, 1e20 und 1) speichert), und dann beginnt die For-Schleife mit der Iteration über das Genexp-Objekt. Effektiv wird die for-Schleife ruft

GENEXP_ITERATOR = `iter(genexp)` 
number = next(GENEXP_ITERATOR) 
# run the loop one time 
number = next(GENEXP_ITERATOR) 
# run the loop one time 
# etc. 

(Man beachte das GENEXP_ITERATOR Objekt an der Code-Ebene nicht sichtbar ist)

next(GENEXP_ITERATOR) versucht, den ersten Wert aus genexp zu ziehen, die dann auf dem Bereich Objekt beginnt Iterieren , zieht einen Wert heraus, quadriert ihn und gibt den Wert als ersten number aus. Beim nächsten Aufruf der For-Schleife next(GENEXP_ITERATOR) zieht der Generatorausdruck den zweiten Wert aus dem Bereichsobjekt heraus, quadriert ihn und gibt ihn für den zweiten Durchlauf der for-Schleife aus. Die erste Zahlengruppe wird nicht mehr im Speicher gehalten.

Dies bedeutet, dass, egal wie viele Elemente im Generator Verständnis, die Speicherauslastung konstant bleibt. Sie können den Generatorausdruck an andere Generatorausdrücke übergeben und lange Pipelines erstellen, die nie große Speichermengen verbrauchen.

def pipeline(filenames): 
    basepath = path.path('/usr/share/stories') 
    fullpaths = (basepath/fn for fn in filenames) 
    realfiles = (fn for fn in fullpaths if os.path.exists(fn)) 
    openfiles = (open(fn) for fn in realfiles) 
    def read_and_close(file): 
     output = file.read(100) 
     file.close() 
     return output 
    prefixes = (read_and_close(file) for file in openfiles) 
    noncliches = (prefix for prefix in prefixes if not prefix.startswith('It was a dark and stormy night') 
    return {prefix[:32]: prefix for prefix in prefixes} 

Zu jeder Zeit, wenn Sie eine Datenstruktur für etwas benötigen, können Sie den Generator Verständnis zu einem anderen Verständnis Typ (wie in der letzten Zeile dieses Beispiels), an welchem ​​Punkt passieren, wird es die Generatoren zwingen um alle verbleibenden Daten auszuwerten, aber wenn Sie das nicht tun, wird der Speicherverbrauch auf das begrenzt, was in einem einzigen Durchlauf über die Generatoren geschieht.

+0

Generatoren wurden als eine Art Verkettung gefördert. – hpaulj

+2

Das OP endet mit einem Wörterbuch, also ist '{x: True für x in input_list wenn x% 2 == 0}' näher an seiner/ihrer Absicht? –

+0

Ich wusste eigentlich nicht, dass Sie 'map comprehensions' bei AlbertoGarcia-Raboso machen könnten, das ist wirklich hilfreich! – ThinkBonobo