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


155

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

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

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

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

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

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


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-итераторов.


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

Не уверен, что имел в виду его вопрос, но я думаю, что 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])

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


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

опечатка в вашем примере: 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.

1

В :nums = [1, 2, 3]

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

Вне:[3, 4, 5]


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


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

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


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

0

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

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

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.