Как сделать несколько аргументов для отображения функции, где один остается неизменным в Python?

155

Допустим, у нас есть функция add следующим образом

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

мы хотим применить функцию карты для массива

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

Семантика я хочу добавить 2 к каждому элементу массива. Но mapфункция также требует списка в третьем аргументе.

Примечание: я добавляю пример добавления для простоты. Моя оригинальная функция намного сложнее. И, конечно, возможность установки значения по умолчанию для yфункции add не подлежит сомнению, так как она будет изменяться при каждом вызове.

Shan
источник
20
это точно так же, как в Lisp: map(add,[1,2,3],[2]*3) в общем случае mapв качестве первого аргумента принимается функция, и если эта функция принимает аргумент K , вы должны использовать итеративный K :addTriple(a,b,c) -> map(addTriple,[...],[...],[...])
watashiSHUN

Ответы:

188

Одним из вариантов является понимание списка:

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

Больше вариантов:

a = [1, 2, 3]

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

import itertools
map(add, a, itertools.repeat(2, len(a)))
Свен Марнах
источник
1
да, но как быстро это по сравнению с функцией карты?
Шань
@Shan: Очень похоже, особенно если add()это нетривиальная функция
Свен Марнах
2
@Shan: Посмотрите на NumPy в этом случае. Если это действительно проблема для вас, разница в скорости между списками пониманий и map()не поможет в любом случае.
Свен Марнач
3
@Shan: Как я уже говорил, взгляните на NumPy. Это может помочь значительно ускорить петли при условии их векторизации.
Свен Марнач
1
@abarnert: Неясно, использует ли OP Python 2 или 3. Чтобы убедиться, что пример работает в Python 2, я включил параметр. (Обратите внимание, что map()ведет себя как zip_longest()в Python 2, в то время как он ведет себя как zip()в Python 3.)
Свен Марнач
60

Документы явно предполагают, что это основное использование для itertools.repeat:

Сделайте итератор, который возвращает объект снова и снова. Работает бесконечно, если не указан аргумент times. Используется в качестве аргумента map()для инвариантных параметров вызываемой функции. Также используется zip()для создания инвариантной части записи кортежа.

И нет никаких оснований для передачи len([1,2,3])в качестве timesаргумента; mapостанавливается, как только первая итерация используется, поэтому бесконечная итерация вполне подходит:

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

Фактически, это эквивалентно примеру для repeatв документах:

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

Это создает хорошее решение для lazy-functions-language-y, которое также отлично читается в терминах Python-итераторов.

abarnert
источник
2
+1 Это должен быть принятый ответ. Разумеется, он распространяется на любое количество параметров с некоторыми константами и другими списками или генераторами. Например: def f(x,y,z): \\ return '.'.join([x,y,z])и затем: list(map(f, list('abc'), repeat('foo'), list('defgh')))возвращается ['a.foo.d', 'b.foo.e', 'c.foo.f'].
Пьер Д
5
В Python 2 необходимо предоставить аргумент длины repeat(), поскольку он map()будет работать до тех пор, пока в этой версии Python не будет исчерпан самый длинный итератор, заполняя Noneвсе пропущенные значения. Сказать, что «нет причины» для передачи параметра, неправильно.
Свен Марнач
Комментарий @SvenMarnach не является второстепенным: поведение Python 3 и Python 2 сильно различается!
Августин
36

Используйте понимание списка.

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

Если вы действительно , действительно , действительно хотите использовать map, дайте ему анонимную функцию в качестве первого аргумента:

map(lambda x: x + 2, [1,2,3])
Фред Фу
источник
16

Карта может содержать несколько аргументов, стандартным способом является

map(add, a, b)

По вашему вопросу это должно быть

map(add, a, [2]*len(a))
Yi.ding
источник
Не уверен, что имел в виду его вопрос, но я думаю, что add принимает значение 2, и эти 2 не являются фиксированными и должны исходить от пользователя, как приходит arg1. так в этом случае, как мы должны назвать add (x, y) под map?
Бимлеш Шарма
15

Правильный ответ проще, чем вы думаете. Просто сделайте:

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

И изменить реализацию добавить, чтобы взять кортеж, т.е.

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

Это может обработать любой сложный случай использования, когда оба параметра добавления являются динамическими.

Саймон Ндунда
источник
14

Иногда я разрешал подобные ситуации (например, используя метод pandas.apply ), используя замыкания

Чтобы использовать их, вы определяете функцию, которая динамически определяет и возвращает обертку для вашей функции, эффективно превращая один из параметров в константу.

Что-то вроде этого:

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

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

Затем add_constant(y)возвращает функцию, которую можно использовать для добавления yк любому заданному значению:

>>> add_constant(2)(3)
5

Что позволяет использовать его в любой ситуации, когда параметры задаются по одному:

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

редактировать

Если вы не хотите писать функцию замыкания где-то еще, у вас всегда есть возможность построить ее на лету с помощью лямбда-функции:

>>> map(lambda x: add(x, 2), [1, 2, 3])
[3, 4, 5]
Карлес Сала
источник
13

Если у вас есть в наличии, я бы рассмотрел использование NumPy. Это очень быстро для этих типов операций:

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

Это предполагает, что ваше реальное приложение выполняет математические операции (которые могут быть векторизованы).

jterrace
источник
10

Если вам действительно нужно использовать функцию карты (например, мое присвоение класса здесь ...), вы можете использовать функцию-оболочку с 1 аргументом, передавая остальную часть исходной в своем теле; т.е.

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


map(myFunc, itterable)

Грязный и уродливый, все еще делает трюк

Тодор Минаков
источник
1
Закрытие, я думаю, это что-то добавляет, плюс один. Подобно частичной функции, как принятый ответ.
Аарон Холл
1
myFuncнужноreturn Func(arg, extraArguments)
PM 2Ring
Я исправлен @ PM2Ring, это правда; обновил код.
Тодор Минаков
где определяется Func? потому что я получаю ошибку имени для Func
Nikhil Mishra
Funcэто название вашей исходной функции - для OP туго было бы, addнапример
Тодор Минаков
6

Я считаю, что вам нужен starmap:

from itertools import starmap


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

list(starmap(test, [(1, 2, 3), (4, 5, 6)]))
Shqi.Yang
источник
опечатка в вашем примере: list (starmap (test, [(1, 2, 3), (4, 5, 6)]))
Teebu
О да. Спасибо :)
Shqi.Yang
1

Чтобы передать несколько аргументов в mapфункцию.

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

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

Здесь q - это функция с несколькими аргументами, которую вызывает map () . Убедитесь, что длина обоих диапазонов, т.е.

len (range(a,a')) and len (range(b,b')) are equal.
SeasonalShot
источник
1

В :nums = [1, 2, 3]

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

Вне:[3, 4, 5]

张小诚
источник
1
Это не дает ответа на вопрос. Вы можете искать похожие вопросы или обратиться к связанным и связанным вопросам в правой части страницы, чтобы найти ответ. Если у вас есть связанный, но другой вопрос, задайте новый вопрос и добавьте ссылку на этот вопрос, чтобы обеспечить контекст. Смотрите: задавайте вопросы, получайте ответы, не
отвлекайтесь
1

Вы можете включить лямбду вместе с картой:

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

Оборачивая вызов функции лямбда-выражением и распаковывая звездочку, вы можете создать карту с произвольным числом аргументов.

Yuukio
источник
Лямбда очень медленная, вместо нее используйте functools
Ван
0

Другой вариант:

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

Этот формат очень полезен при вызове нескольких функций.

sudoman
источник