373 Stimmen

Verwendung von Filter, Map und Reduce in Python 3

filter , map et reduce funktionieren perfekt in Python 2. Hier ist ein Beispiel:

>>> def f(x):
        return x % 2 != 0 and x % 3 != 0
>>> filter(f, range(2, 25))
[5, 7, 11, 13, 17, 19, 23]

>>> def cube(x):
        return x*x*x
>>> map(cube, range(1, 11))
[1, 8, 27, 64, 125, 216, 343, 512, 729, 1000]

>>> def add(x,y):
        return x+y
>>> reduce(add, range(1, 11))
55

In Python 3 erhalte ich jedoch die folgenden Ausgaben:

>>> filter(f, range(2, 25))
<filter object at 0x0000000002C14908>

>>> map(cube, range(1, 11))
<map object at 0x0000000002C82B70>

>>> reduce(add, range(1, 11))
Traceback (most recent call last):
  File "<pyshell#8>", line 1, in <module>
    reduce(add, range(1, 11))
NameError: name 'reduce' is not defined

Ich wäre dankbar, wenn mir jemand erklären könnte, warum das so ist.

Screenshot des Codes zur weiteren Verdeutlichung:

IDLE sessions of Python 2 and 3 side-by-side

387voto

nhahtdh Punkte 54526

Sie können über die Änderungen lesen in Was ist neu in Python 3.0 . Sie sollten es gründlich lesen, wenn Sie von 2.x auf 3.x umsteigen, da sich viel geändert hat.

Die gesamte Antwort hier sind Zitate aus der Dokumentation.

Ansichten und Iteratoren anstelle von Listen

Einige bekannte APIs geben keine Listen mehr zurück:

  • [...]
  • map() y filter() Iteratoren zurückgeben. Wenn Sie wirklich eine Liste benötigen, ist eine schnelle Lösung z.B. list(map(...)) Eine bessere Lösung ist jedoch oft die Verwendung einer Listenverarbeitung (vor allem, wenn der ursprüngliche Code Lambda verwendet) oder das Umschreiben des Codes, so dass er überhaupt keine Liste benötigt. Besonders knifflig ist map() für die Nebeneffekte der Funktion aufgerufen werden; die korrekte Umwandlung ist die Verwendung eines regulären for Schleife (da die Erstellung einer Liste nur unnötig wäre).
  • [...]

Eingebaut

  • [...]
  • Entfernt reduce() . Verwenden Sie functools.reduce() wenn Sie es wirklich brauchen; in 99 Prozent der Fälle ist jedoch eine explizite for Schleife ist besser lesbar.
  • [...]

104voto

Joshua D. Boyd Punkte 4708

Die Funktionalität von map y filter wurde absichtlich so geändert, dass es Iteratoren zurückgibt, und reduce wurde aus der Liste der eingebauten Funktionen entfernt und in functools.reduce .

Also, für filter y map können Sie sie einpacken mit list() um die Ergebnisse zu sehen, wie Sie es zuvor getan haben.

>>> def f(x): return x % 2 != 0 and x % 3 != 0
...
>>> list(filter(f, range(2, 25)))
[5, 7, 11, 13, 17, 19, 23]
>>> def cube(x): return x*x*x
...
>>> list(map(cube, range(1, 11)))
[1, 8, 27, 64, 125, 216, 343, 512, 729, 1000]
>>> import functools
>>> def add(x,y): return x+y
...
>>> functools.reduce(add, range(1, 11))
55
>>>

Die Empfehlung lautet nun, dass Sie Ihre Verwendung von map und filter durch Generatorenausdrücke oder List Comprehensions ersetzen. Beispiel:

>>> def f(x): return x % 2 != 0 and x % 3 != 0
...
>>> [i for i in range(2, 25) if f(i)]
[5, 7, 11, 13, 17, 19, 23]
>>> def cube(x): return x*x*x
...
>>> [cube(i) for i in range(1, 11)]
[1, 8, 27, 64, 125, 216, 343, 512, 729, 1000]
>>>

Man sagt, dass for-Schleifen in 99 Prozent der Fälle einfacher zu lesen sind als reduce, aber ich würde mich einfach an functools.reduce .

編集部 : Die Zahl von 99 Prozent stammt direkt aus der Was ist neu in Python 3.0 Seite verfasst von Guido van Rossum.

12voto

Als Ergänzung zu den anderen Antworten klingt dies nach einem guten Anwendungsfall für einen Kontextmanager, der die Namen dieser Funktionen in solche umwandelt, die eine Liste zurückgeben und Folgendes einführt reduce im globalen Namespace.

Eine schnelle Umsetzung könnte wie folgt aussehen:

from contextlib import contextmanager    

@contextmanager
def noiters(*funcs):
    if not funcs: 
        funcs = [map, filter, zip] # etc
    from functools import reduce
    globals()[reduce.__name__] = reduce
    for func in funcs:
        globals()[func.__name__] = lambda *ar, func = func, **kwar: list(func(*ar, **kwar))
    try:
        yield
    finally:
        del globals()[reduce.__name__]
        for func in funcs: globals()[func.__name__] = func

Mit einer Verwendung, die wie folgt aussieht:

with noiters(map):
    from operator import add
    print(reduce(add, range(1, 20)))
    print(map(int, ['1', '2']))

Welche Drucke:

190
[1, 2]

Nur meine 2 Cents :-)

10voto

Bikash Singh Punkte 261

Da die reduce Methode wurde aus der eingebauten Funktion von Python3 entfernt, vergessen Sie nicht, die functools in Ihrem Code. Schauen Sie sich bitte das folgende Codefragment an.

import functools
my_list = [10,15,20,25,35]
sum_numbers = functools.reduce(lambda x ,y : x+y , my_list)
print(sum_numbers)

6voto

Daniel Punkte 3540

Einer der Vorteile von map, filter und reduce ist, dass sie lesbar werden, wenn man sie miteinander "verkettet", um etwas Komplexes zu tun. Die eingebaute Syntax ist jedoch nicht lesbar und alles ist "rückwärts". Ich schlage daher vor, die PyFunctional Paket ( https://pypi.org/project/PyFunctional/ ). Hier ist ein Vergleich zwischen den beiden:

flight_destinations_dict = {'NY': {'London', 'Rome'}, 'Berlin': {'NY'}}

PyFunctional-Version

Sehr gut lesbare Syntax. Sie können sagen:

"Ich habe eine Reihe von Flugzielen. Daraus möchte ich den den Diktatschlüssel, wenn die Stadt in den Diktatwerten enthalten ist. Filtern Sie schließlich die leeren Listen herausfiltern, die ich dabei erstellt habe."

from functional import seq  # PyFunctional package to allow easier syntax

def find_return_flights_PYFUNCTIONAL_SYNTAX(city, flight_destinations_dict):
    return seq(flight_destinations_dict.items()) \
        .map(lambda x: x[0] if city in x[1] else []) \
        .filter(lambda x: x != []) \

Standard-Python-Version

Es ist alles verkehrt herum. Sie müssen sagen:

"OK, also, da ist eine Liste. Ich möchte leere Listen aus ihr herausfiltern. Warum? Weil ich zuerst den Diktatschlüssel bekommen habe, wenn die Stadt in den Diktatwerten war. Oh, die Liste, mit der ich das mache, ist flight_destinations_dict".

def find_return_flights_DEFAULT_SYNTAX(city, flight_destinations_dict):
    return list(
        filter(lambda x: x != [],
               map(lambda x: x[0] if city in x[1] else [], flight_destinations_dict.items())
               )
    )

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X