Como fazer vários argumentos para mapear a função em que um permanece o mesmo em python?


155

Vamos dizer que temos uma função add da seguinte forma

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

queremos aplicar a função de mapa para uma matriz

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

A semântica é que eu quero adicionar 2 para todos os elementos da matriz. Mas a mapfunção também requer uma lista no terceiro argumento.

Nota: Estou colocando o exemplo add por simplicidade. Minha função original é muito mais complicada. E é claro que a opção de definir o valor padrão da yfunção add está fora de questão, pois ela será alterada para cada chamada.


20
este é exatamente o mesmo que em Lisp: map(add,[1,2,3],[2]*3) em geral mapleva em uma função como seu primeiro argumento, e se esta função leva K argumento, você tem que acompanhar com K iterable:addTriple(a,b,c) -> map(addTriple,[...],[...],[...])
watashiSHUN

Respostas:


188

Uma opção é a compreensão da lista:

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

Mais opções:

a = [1, 2, 3]

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

import itertools
map(add, a, itertools.repeat(2, len(a)))

1
Sim, mas qual é a velocidade em comparação com a função de mapa?
31512 Shan

@Shan: Muito semelhante, especialmente se add()é uma função não-trivial
Sven Marnach

2
@ Shan: Dê uma olhada no NumPy neste caso. Se isso realmente é um problema para você, a diferença de velocidade entre as compreensões da lista e map()não ajudará de qualquer maneira.
Sven Marnach

3
@ Shan: Como eu disse antes, dê uma olhada no NumPy. Pode ajudar a acelerar os loops de forma conderível, desde que possam ser vetorizados.
Sven Marnach

1
@abarnert: Não está claro se o OP está usando o Python 2 ou 3. Para garantir que o exemplo funcione no Python 2, incluí o parâmetro. (Note-se que map()se comporta como zip_longest()em Python 2, enquanto que ele se comporta como zip()em Python 3.)
Sven Marnach

60

Os documentos sugerem explicitamente que esse é o principal uso para itertools.repeat:

Faça um iterador que retorne o objeto repetidamente. É executado indefinidamente, a menos que o argumento times seja especificado. Usado como argumento map()para parâmetros invariantes para a função chamada. Também usado com zip()para criar uma parte invariável de um registro de tupla.

E não há razão para passar len([1,2,3])como timesargumento; mappara assim que o primeiro iterável é consumido, portanto, um iterável infinito é perfeitamente adequado:

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

De fato, isso é equivalente ao exemplo repeatdos documentos:

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

Isso cria uma boa solução de linguagem funcional e preguiçosa que também é perfeitamente legível em termos de iterador Python.


2
+1 Esta deve ser a resposta aceita. Ele se estende a qualquer número de parâmetros, é claro, com algumas listas ou geradores constantes e outros. Por exemplo: def f(x,y,z): \\ return '.'.join([x,y,z])e depois: list(map(f, list('abc'), repeat('foo'), list('defgh')))retorna ['a.foo.d', 'b.foo.e', 'c.foo.f'].
Pierre D

5
No Python 2, é necessário fornecer o argumento length repeat(), pois map()será executado até que o iterador mais longo se esgote nessa versão do Python, preenchendo Nonetodos os valores ausentes. Dizer que "não há razão" para passar o parâmetro está errado.
Sven Marnach

O comentário de @SvenMarnach não é secundário: o comportamento do Python 3 e do Python 2 varia drasticamente!
Agustín

36

Use uma lista de compreensão.

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

Se você realmente , realmente , realmente deseja usar map, dê a ela uma função anônima como o primeiro argumento:

map(lambda x: x + 2, [1,2,3])

16

O mapa pode conter vários argumentos, a maneira padrão é

map(add, a, b)

Na sua pergunta, deve ser

map(add, a, [2]*len(a))

Não tenho certeza do que sua pergunta significava, mas acho que adicione o valor aceite 2 e esses 2 não são fixos e devem vir de usuários como o arg1 está chegando. Então, neste caso, como devemos chamar add (x, y) no mapa?
Bimlesh Sharma

15

A resposta correta é mais simples do que você pensa. Simplesmente faça:

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

E mude a implementação de add para fazer uma tupla, ie

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

Isso pode lidar com qualquer caso de uso complicado em que ambos os parâmetros de adição sejam dinâmicos.


14

Às vezes, resolvia situações semelhantes (como usar o método pandas.apply ) usando closures

Para usá-los, você define uma função que define e retorna dinamicamente um wrapper para sua função, tornando efetivamente um dos parâmetros uma constante.

Algo assim:

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

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

Em seguida, add_constant(y)retorna uma função que pode ser usada para adicionar ya qualquer valor:

>>> add_constant(2)(3)
5

O que permite usá-lo em qualquer situação em que os parâmetros sejam dados um de cada vez:

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

editar

Se você não quiser escrever a função de fechamento em outro lugar, sempre terá a possibilidade de construí-la em tempo real usando uma função lambda:

>>> map(lambda x: add(x, 2), [1, 2, 3])
[3, 4, 5]

13

Se você tiver disponível, eu consideraria usar numpy. É muito rápido para esses tipos de operações:

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

Isso pressupõe que sua aplicação real esteja executando operações matemáticas (que podem ser vetorizadas).


10

Se você realmente precisa usar a função map (como minha tarefa de classe aqui ...), você pode usar uma função wrapper com 1 argumento, passando o restante para o original em seu corpo; ou seja:

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


map(myFunc, itterable)

Sujo e feio, ainda faz o truque


1
Um fechamento, acho que isso acrescenta algo, mais um. Semelhante a uma função parcial, como a resposta aceita.
Aaron Hall

1
myFuncprecisareturn Func(arg, extraArguments)
PM 2Ring

Estou corrigido @ PM2Ring, isso é verdade; atualizou o código.
Todor Minakov

Onde o Func está definido? porque eu estou recebendo um erro de nome para o Func #
Nikhil Mishra

Funcé o nome da sua função original - para apertado OP seria addpor exemplo
Todor Minakov

6

Acredito que starmap é o que você precisa:

from itertools import starmap


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

list(starmap(test, [(1, 2, 3), (4, 5, 6)]))

erro de digitação no seu exemplo: list (starmap (teste, [(1, 2, 3), (4, 5, 6)])))
Teebu

Ai sim. Obrigado :)
Shqi.Yang

1

Para passar vários argumentos para uma mapfunção.

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

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

Aqui q é função com múltiplos argumentos que map () chama. Verifique se o comprimento de ambos os intervalos

len (range(a,a')) and len (range(b,b')) are equal.

1

No :nums = [1, 2, 3]

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

Fora:[3, 4, 5]


1
Isso não fornece uma resposta para a pergunta. Você pode procurar por perguntas semelhantes ou consultar as perguntas relacionadas e relacionadas no lado direito da página para encontrar uma resposta. Se você tiver uma pergunta relacionada, mas diferente, faça uma nova pergunta e inclua um link para ajudar a fornecer contexto. Veja: Faça perguntas, obter respostas, sem distrações
Tim Diekmann

1

Você pode incluir lambda junto com o mapa:

list(map(lambda a: a+2, [1, 2, 3]))

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

Envolvendo a chamada de função com um lambda e usando a descompactação em estrela, é possível mapear com número arbitrário de argumentos.


lambda é muito lento, use functools
Wang

0

Outra opção é:

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

Este formato é muito útil ao chamar várias funções.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.