Verwenden von Filtern, Zuordnen und Reduzieren in Python 3


321

filter, mapUnd reducearbeitet 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ürde mich freuen, wenn mir jemand erklären könnte, warum das so ist.

Screenshot des Codes für weitere Klarheit:

IDLE-Sitzungen von Python 2 und 3 nebeneinander


1
Kurz gesagt, Liste ist nicht der einzige Datentyp. Wenn Sie eine Liste möchten, sagen Sie, Sie möchten eine Liste. Aber in den meisten Fällen möchten Sie trotzdem etwas anderes.
Veky

Antworten:


345

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

Die ganze Antwort hier sind Zitate aus der Dokumentation.

Ansichten und Iteratoren anstelle von Listen

Einige bekannte APIs geben keine Listen mehr zurück:

  • [...]
  • map()und filter()Rückgabe von Iteratoren. Wenn Sie wirklich eine Liste benötigen, ist eine schnelle Lösung zB list(map(...)), aber eine bessere Lösung besteht häufig darin, ein Listenverständnis zu verwenden (insbesondere wenn der ursprüngliche Code Lambda verwendet) oder den Code neu zu schreiben, sodass überhaupt keine Liste erforderlich ist. Besonders knifflig wird map()für die Nebenwirkungen der Funktion aufgerufen; Die richtige Transformation besteht darin, eine reguläre forSchleife zu verwenden (da das Erstellen einer Liste nur verschwenderisch wäre).
  • [...]

Builtins

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

21
list(map(...) Überall hinzufügen ... wie in aller Welt ist das für eine bessere Lesbarkeit ... pythonscheint nicht mit der progressiven / Streaming-Anwendung von Funktionskombinatoren umzugehen. Andere Sprachen Ich kann ein Dutzend Operationen mit einer Sammlung hintereinander verketten und sie ist lesbar. Hier? Was willst du - ein Dutzend verschachtelt in?
Javadba

11
Wenn Sie in einem zwingenden Kontext arbeiten, ist eine for-Schleife wahrscheinlich die besser lesbare Option. Aber es gibt gute Gründe, einen funktionalen Kontext zu bevorzugen - und davon abzuweichen, um zum Verfahren zurückzukehren, kann verdammt hässlich sein.
MatrixManAtYrService

2
@javadba Sind Sie sicher, dass Sie in einer "Streaming-Anwendung" den listAnruf überhaupt hinzufügen müssen ? Ich dachte, die Bedeutung von "Streaming" ist "es wird überhaupt keine Liste erstellt; verarbeiten Sie jedes Element der Eingabe vollständig, bevor Sie mit dem nächsten fortfahren".
Unverderbliche Nacht

@MatrixManAtYrService Wenn Sie sicher sind, dass das Python 2-Verhalten Ihren Anforderungen entspricht, können Sie es jederzeit neu definieren map.
Unverderbliche Nacht

6
Ich kann immer noch nicht verstehen, wie ein Lesbarkeitsargument zu einer solchen Änderung führt. Wenn es aus Leistungsgründen wäre, könnte ich verstehen ...
Minato

86

Die Funktionalität von mapund filterwurde absichtlich geändert, um Iteratoren zurückzugeben, und die Reduzierung wurde aus der integrierten Funktion entfernt und eingefügt functools.reduce.

Sie können sie also für filterund mapumschließen list(), um die Ergebnisse wie zuvor anzuzeigen.

>>> 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 Generatorausdrücke oder Listenverständnisse 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]
>>>

Sie sagen, dass for-Loops zu 99 Prozent leichter zu lesen sind als zu reduzieren, aber ich würde einfach dabei bleiben functools.reduce.

Bearbeiten : Die 99-Prozent-Zahl wird direkt von der Seite " Was ist neu in Python 3.0?" Von Guido van Rossum abgerufen.


5
Sie müssen keine zusätzlichen Funktionen für das Listenverständnis erstellen. Verwenden Sie einfach[i*i*i for i in range(1,11)]
Xiao

2
Sie sind absolut richtig. Ich habe die Funktion in den Beispielen für das Listenverständnis beibehalten, damit sie den Filter- / Kartenbeispielen ähnelt.
Joshua D. Boyd

5
i ** 3 ist auch äquivalent zu i * i * i
Breezer

5
@Breezer wird tatsächlich i**3anrufen i.__pow__(3)und i*i*i i.__mul__(i).__mul__(i)(oder so ähnlich). Bei Ints spielt es keine Rolle, aber bei Numpy-Zahlen / benutzerdefinierten Klassen kann es sogar zu unterschiedlichen Ergebnissen kommen.
Syntonym

1
Ich habe bemerkt, dass Schmerz immer dann wahrscheinlich ist , wenn wir hören, dass "Guido die Entscheidung X getroffen hat" . Dies ist ein großartiges Beispiel: das list(list(list(.. )))zu tun, was in Python bereits ausführlich war.
Javadba

12

Als Ergänzung zu den anderen Antworten klingt dies wie ein guter Anwendungsfall für einen Kontextmanager, der die Namen dieser Funktionen denen zuordnet, die eine Liste zurückgeben und reducein den globalen Namespace einführen .

Eine schnelle Implementierung könnte folgendermaßen 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 so 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 Cent :-)


1
pythonals Sprache ist ein Chaos - aber es v gut bis ausgezeichnet Bibliotheken hat: numpy, pandas, statsmodelsund Freunde .. hatte ich buliding Bequemlichkeit Bibliotheken wurden wie Sie hier zeigen , den Schmerz der Muttersprache zu reduzieren - aber haben die Energie verloren und versuchen , nicht zu weit weg von einem data.frame/ datatableoder xarray. Aber ein
großes Lob

7

reduceVergessen Sie nicht, die Methode functoolsin Ihren Code zu importieren, da die Methode aus der integrierten Funktion von Python3 entfernt wurde . Bitte schauen Sie sich das Code-Snippet unten an.

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

2

Hier sind die Beispiele für Funktionen zum Filtern, Zuordnen und Reduzieren.

Zahlen = [10,11,12,22,34,43,54,34,67,87,88,98,99,87,44,66]

//Filter

oddNumbers = Liste (Filter (Lambda x: x% 2! = 0, Zahlen))

print (oddNumbers)

//Karte

multiplyOf2 = Liste (Karte (Lambda x: x * 2, Zahlen))

print (multiplyOf2)

//Reduzieren

Die Reduktionsfunktion wurde, da sie nicht häufig verwendet wird, aus den in Python 3 integrierten Funktionen entfernt. Sie ist weiterhin im functools-Modul verfügbar, sodass Sie Folgendes tun können:

von functools import reduzieren

sumOfNumbers = reduzieren (Lambda x, y: x + y, Zahlen)

print (sumOfNumbers)


Geben Sie eine +1 für die Formatierung
Byron Coetsee

0

Einer der Vorteile von Map, Filter und Reduce ist, wie lesbar sie werden, wenn Sie sie zu einer komplexen Aufgabe "verketten". Die eingebaute Syntax ist jedoch nicht lesbar und "rückwärts". Daher schlage ich vor, das PyFunctionalPaket zu verwenden ( https://pypi.org/project/PyFunctional/ ). Hier ist ein Vergleich der beiden:

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

PyFunctional Version

Sehr gut lesbare Syntax. Sie können sagen:

"Ich habe eine Folge von Flugzielen. Von diesen möchte ich den Diktatschlüssel erhalten, wenn die Stadt in den Diktatwerten enthalten ist. Filtern Sie schließlich die leeren Listen heraus, 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 rückwärts. Sie müssen sagen:

"OK, also gibt es eine Liste. Ich möchte leere Listen daraus herausfiltern. Warum? Weil ich zuerst den Diktatschlüssel erhalten habe, wenn die Stadt in den Diktatwerten war. Oh, die Liste, für die 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())
               )
    )
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.