Comment faire plusieurs arguments pour mapper la fonction où l'un reste le même en python?

155

Disons que nous avons une fonction ajouter comme suit

def add(x, y):
    return x + y

nous voulons appliquer la fonction de carte pour un tableau

map(add, [1, 2, 3], 2)

La sémantique est que je veux ajouter 2 à chaque élément du tableau. Mais la mapfonction nécessite également une liste dans le troisième argument.

Remarque: je mets l'exemple d'ajout pour plus de simplicité. Ma fonction d'origine est beaucoup plus compliquée. Et bien sûr, l'option de définir la valeur par défaut de la yfonction d'ajout est hors de question car elle sera modifiée à chaque appel.

Shan
la source
20
c'est exactement la même chose qu'en Lisp: map(add,[1,2,3],[2]*3) en général mapprend une fonction comme premier argument, et si cette fonction prend l' argument K , vous devez suivre avec K iterable:addTriple(a,b,c) -> map(addTriple,[...],[...],[...])
watashiSHUN

Réponses:

188

Une option est une compréhension de liste:

[add(x, 2) for x in [1, 2, 3]]

Plus d'options:

a = [1, 2, 3]

import functools
map(functools.partial(add, y=2), a)

import itertools
map(add, a, itertools.repeat(2, len(a)))
Sven Marnach
la source
1
oui, mais à quelle vitesse est-ce par rapport à la fonction de carte?
Shan
@Shan: Très similaire, surtout s'il add()s'agit d'une fonction non triviale
Sven Marnach
2
@Shan: Jetez un œil à NumPy dans ce cas. Si c'est vraiment un problème pour vous, la différence de vitesse entre les compréhensions de liste map()n'aidera pas dans les deux cas.
Sven Marnach
3
@Shan: Comme je l'ai déjà dit, jetez un œil à NumPy. Cela pourrait aider à accélérer les boucles de manière conderable, à condition qu'elles puissent être vectorisées.
Sven Marnach
1
@abarnert: On ne sait pas si l'OP utilise Python 2 ou 3. Pour m'assurer que l'exemple fonctionne dans Python 2, j'ai inclus le paramètre. (Notez qu'il map()se comporte comme zip_longest()dans Python 2, alors qu'il se comporte comme zip()dans Python 3.)
Sven Marnach
60

La documentation suggère explicitement qu'il s'agit de l'utilisation principale pour itertools.repeat:

Créez un itérateur qui renvoie un objet encore et encore. S'exécute indéfiniment sauf si l'argument times est spécifié. Utilisé comme argument map()pour les paramètres invariants de la fonction appelée. Également utilisé avec zip()pour créer une partie invariante d'un enregistrement de tuple.

Et il n'y a aucune raison de passer len([1,2,3])pour l' timesargument; maps'arrête dès que le premier itérable est consommé, donc un itérable infini convient parfaitement:

>>> from operator import add
>>> from itertools import repeat
>>> list(map(add, [1,2,3], repeat(4)))
[5, 6, 7]

En fait, cela équivaut à l'exemple de repeatla documentation:

>>> list(map(pow, range(10), repeat(2)))
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

Cela en fait une belle solution de langage fonctionnel paresseux qui est également parfaitement lisible en termes d'itérateur Python.

Abarnert
la source
2
+1 Cela devrait être la réponse acceptée. Il s'étend à n'importe quel nombre de paramètres, bien sûr, avec certaines constantes et d'autres listes ou générateurs. Par exemple: def f(x,y,z): \\ return '.'.join([x,y,z])et ensuite: list(map(f, list('abc'), repeat('foo'), list('defgh')))retourne ['a.foo.d', 'b.foo.e', 'c.foo.f'].
Pierre D
5
Dans Python 2, il est nécessaire de fournir l'argument de longueur à repeat(), car map()il s'exécutera jusqu'à ce que l' itérateur le plus long soit épuisé dans cette version de Python, en remplissant Nonetoutes les valeurs manquantes. Dire qu '"il n'y a pas de raison" de passer le paramètre est faux.
Sven Marnach
Le commentaire de @SvenMarnach n'est pas mineur: le comportement de Python 3 et Python 2 varie drastiquement!
Agustín
36

Utilisez une compréhension de liste.

[x + 2 for x in [1, 2, 3]]

Si vous voulez vraiment , vraiment , vraiment utiliser map, donnez-lui une fonction anonyme comme premier argument:

map(lambda x: x + 2, [1,2,3])
Fred Foo
la source
16

La carte peut contenir plusieurs arguments, la méthode standard est

map(add, a, b)

Dans votre question, cela devrait être

map(add, a, [2]*len(a))
Yi.ding
la source
Je ne sais pas ce que signifiait sa question, mais je pense que la valeur add accept 2 et ces 2 ne sont pas fixes et devraient provenir de l'utilisateur comme arg1 arrive. alors dans ce cas, comment appeler add (x, y) sous map?
Bimlesh Sharma
15

La bonne réponse est plus simple que vous ne le pensez. Faites simplement:

map(add, [(x, 2) for x in [1,2,3]])

Et changez l'implémentation de add pour prendre un tuple ie

def add(t):
   x, y = t
   return x+y

Cela peut gérer tout cas d'utilisation compliqué où les deux paramètres d'ajout sont dynamiques.

Simon Ndunda
la source
14

Parfois, j'ai résolu des situations similaires (telles que l'utilisation de la méthode pandas.apply ) en utilisant des fermetures

Pour les utiliser, vous définissez une fonction qui définit et retourne dynamiquement un wrapper pour votre fonction, faisant de l'un des paramètres une constante.

Quelque chose comme ça:

def add(x, y):
   return x + y

def add_constant(y):
    def f(x):
        return add(x, y)
    return f

Ensuite, add_constant(y)renvoie une fonction qui peut être utilisée pour ajouter yà n'importe quelle valeur donnée:

>>> add_constant(2)(3)
5

Ce qui vous permet de l'utiliser dans n'importe quelle situation où les paramètres sont donnés un à la fois:

>>> map(add_constant(2), [1,2,3])
[3, 4, 5]

Éditer

Si vous ne voulez pas avoir à écrire la fonction de fermeture ailleurs, vous avez toujours la possibilité de la construire à la volée en utilisant une fonction lambda:

>>> map(lambda x: add(x, 2), [1, 2, 3])
[3, 4, 5]
Carles Sala
la source
13

Si vous l'avez disponible, j'envisagerais d'utiliser numpy. C'est très rapide pour ces types d'opérations:

>>> import numpy
>>> numpy.array([1,2,3]) + 2
array([3, 4, 5])

Cela suppose que votre application réelle effectue des opérations mathématiques (qui peuvent être vectorisées).

jterrace
la source
10

Si vous avez vraiment besoin d'utiliser la fonction map (comme mon affectation de classe ici ...), vous pouvez utiliser une fonction wrapper avec 1 argument, en passant le reste à l'original dans son corps; c'est à dire :

extraArguments = value
def myFunc(arg):
    # call the target function
    return Func(arg, extraArguments)


map(myFunc, itterable)

Sale et laid, fait toujours l'affaire

Todor Minakov
la source
1
Une fermeture, je pense que cela ajoute quelque chose, plus un. Similaire à une fonction partielle, comme l'a fait la réponse acceptée.
Aaron Hall
1
myFuncdoitreturn Func(arg, extraArguments)
PM 2Ring
Je suis corrigé @ PM2Ring, c'est vrai; mis à jour le code.
Todor Minakov
où est défini Func? parce que j'obtiens une erreur de nom pour Func
Nikhil Mishra
Funcest le nom de votre fonction d'origine - pour OP tight serait addpar exemple
Todor Minakov
6

Je crois que la carte stellaire est ce dont vous avez besoin:

from itertools import starmap


def test(x, y, z):
    return x + y + z

list(starmap(test, [(1, 2, 3), (4, 5, 6)]))
Shqi.Yang
la source
faute de frappe dans votre exemple: list (starmap (test, [(1, 2, 3), (4, 5, 6)]))
Teebu
Oh oui. Merci :)
Shqi.Yang
1

Pour passer plusieurs arguments à une mapfonction.

def q(x,y):
    return x*y

print map (q,range(0,10),range(10,20))

Ici, q est une fonction avec plusieurs arguments que map () appelle. Assurez-vous que la longueur des deux plages soit

len (range(a,a')) and len (range(b,b')) are equal.
Saisonnier
la source
1

Dans :nums = [1, 2, 3]

Dans :map(add, nums, [2]*len(nums))

En dehors:[3, 4, 5]

张小诚
la source
1
Cela ne répond pas à la question. Vous pouvez rechercher des questions similaires ou vous référer aux questions associées et liées sur le côté droit de la page pour trouver une réponse. Si vous avez une question connexe mais différente, posez une nouvelle question et incluez un lien vers celle-ci pour vous aider à fournir un contexte. Voir: Posez des questions, obtenez des réponses, pas de distractions
Tim Diekmann
1

Vous pouvez inclure lambda avec la carte:

list(map(lambda a: a+2, [1, 2, 3]))
Hanish Madan
la source
1
def func(a, b, c, d):
 return a + b * c % d
map(lambda x: func(*x), [[1,2,3,4], [5,6,7,8]])

En enveloppant l'appel de fonction avec un lambda et en utilisant la décompression en étoile, vous pouvez faire une carte avec un nombre arbitraire d'arguments.

Yuukio
la source
lambda est très lent, utilisez plutôt functools
Wang
0

Une autre option est:

results = []
for x in [1,2,3]:
    z = add(x,2)
    ...
    results += [f(z,x,y)]

Ce format est très utile lors de l'appel de plusieurs fonctions.

sudoman
la source