353 votes

Comment utiliser les fonctions filter, map et reduce en Python 3 ?

filter , map y reduce fonctionnent parfaitement dans Python 2. Voici un exemple :

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

Mais dans Python 3, je reçois les sorties suivantes :

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

J'apprécierais que quelqu'un puisse m'expliquer pourquoi.

Capture d'écran du code pour plus de clarté :

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

1 votes

En bref, la liste n'est pas le seul type de données. Si vous voulez une liste, dites que vous voulez une liste. Mais dans la plupart des cas, vous voulez quelque chose d'autre de toute façon.

376voto

nhahtdh Points 28167

Vous pouvez lire les changements dans Nouveautés de Python 3.0 . Vous devriez le lire attentivement lorsque vous passez de la version 2.x à la version 3.x, car beaucoup de choses ont été modifiées.

Toute la réponse ici est une citation de la documentation.

Vues et itérateurs au lieu de listes

Certaines API bien connues ne renvoient plus de listes :

  • [...]
  • map() y filter() renvoient des itérateurs. Si vous avez vraiment besoin d'une liste, une solution rapide est, par exemple, la suivante list(map(...)) Mais une meilleure solution est souvent d'utiliser une liste de compréhension (surtout lorsque le code original utilise lambda), ou de réécrire le code pour qu'il n'ait pas besoin de liste du tout. Particulièrement délicat est map() invoquée pour les effets secondaires de la fonction ; la transformation correcte consiste à utiliser une fonction ordinaire for (puisque la création d'une liste ne serait que du gaspillage).
  • [...]

Builtins

  • [...]
  • Supprimé reduce() . Utilisez functools.reduce() si vous en avez vraiment besoin ; cependant, dans 99% des cas, une version explicite for La boucle est plus lisible.
  • [...]

31 votes

Ajout de list(map(...) partout comment diable cela peut-il aider à la lisibilité python ne semble pas pouvoir gérer l'application progressive / en continu des combinateurs fonctionnels. Dans d'autres langages, je peux enchaîner une douzaine d'opérations sur une collection à la suite et c'est lisible. Ici ? que voulez-vous - une douzaine d'opérations imbriquées de façon in ? ?

11 votes

Si vous travaillez dans un contexte impératif, une boucle for est probablement l'option la plus lisible. Mais il y a de bonnes raisons de préférer un contexte fonctionnel - et s'en détacher pour revenir au procédural peut être assez laid.

3 votes

@javadba Êtes-vous sûr que dans une "application de streaming", vous devez ajouter l'élément list appeler du tout ? Je pensais que le sens de "streaming" était "aucune liste n'est créée du tout ; traiter chaque élément de l'entrée complètement avant de passer au suivant".

98voto

Joshua D. Boyd Points 1876

La fonctionnalité de map y filter a été intentionnellement modifié pour renvoyer des itérateurs, et reduce n'est plus un élément intégré et a été placé dans le module functools.reduce .

Donc, pour filter y map vous pouvez les envelopper avec list() pour voir les résultats comme avant.

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

Il est maintenant recommandé de remplacer l'utilisation de map et filter par des expressions génératrices ou des compréhensions de listes. Exemple :

>>> 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]
>>>

On dit que les boucles for sont 99 % du temps plus faciles à lire que les boucles reduce, mais je m'en tiendrais simplement à functools.reduce .

Modifier : Le chiffre de 99% est tiré directement de la Quoi de neuf dans Python 3.0 ? page rédigée par Guido van Rossum.

5 votes

Il n'est pas nécessaire de créer des fonctions supplémentaires dans les compréhensions de listes. Utilisez simplement [i*i*i for i in range(1,11)]

2 votes

Vous avez tout à fait raison. J'ai gardé la fonction dans les exemples de compréhension de liste pour qu'elle reste similaire aux exemples de filtre/map.

5 votes

I**3 est également équivalent à i*i*i

12voto

Jim Points 8793

En complément des autres réponses, cela semble être un bon cas d'utilisation pour un gestionnaire de contexte qui réaffectera les noms de ces fonctions à celles qui renvoient une liste et introduira reduce dans l'espace de nom global.

Une mise en œuvre rapide pourrait ressembler à ceci :

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

Avec une utilisation qui ressemble à ceci :

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

Qui imprime :

190
[1, 2]

Juste mes deux cents :-)

9voto

Bikash Singh Points 231

Depuis le reduce a été supprimée de la fonction intégrée de Python3, n'oubliez pas d'importer la fonction functools dans votre code. Veuillez consulter l'extrait de code ci-dessous.

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

4voto

Daniel Points 1987

L'un des avantages de map, filter et reduce est leur lisibilité lorsque vous les "enchaînez" pour faire quelque chose de complexe. Cependant, la syntaxe intégrée n'est pas lisible et est entièrement "à l'envers". Je suggère donc d'utiliser la fonction PyFunctional paquet ( https://pypi.org/project/PyFunctional/ ). Voici une comparaison des deux :

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

Version de PyFunctional

Syntaxe très lisible. Vous pouvez le dire :

"J'ai une séquence de destinations de vol. Dont je veux obtenir la clé du dict si la ville est dans les valeurs du dict. Enfin, filtrer les listes vides que j'ai créées dans le processus."

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 != []) \

Version par défaut de Python

C'est tout à l'envers. Tu dois dire :

"OK, donc, il y a une liste. Je veux en filtrer les listes vides. Pourquoi ? Parce que j'ai d'abord obtenu la clé du dict si la ville était dans les valeurs du dict. Oh, la liste sur laquelle je fais ça est 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())
               )
    )

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X