Як зробити декілька аргументів для відображення функції, коли один залишається таким же у python?


155

Скажімо, у нас є функція додавання наступним чином

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

ми хочемо застосувати функцію map для масиву

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

Семантика - це те, що я хочу додати 2 до кожного елемента масиву. Але mapфункція потребує списку і в третьому аргументі.

Примітка. Я додаю приклад для простоти. Моя оригінальна функція набагато складніша. І звичайно, параметр встановлення значення за замовчуванням yфункції add не викликає сумнівів, оскільки це буде змінено для кожного дзвінка.


20
це точно так само, як у Lisp: map(add,[1,2,3],[2]*3) взагалі mapприймає функцію як перший аргумент, і якщо ця функція приймає аргумент K , ви повинні продовжувати роботу з K iterable: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: Незрозуміло, чи використовує ОП Python 2 або 3. Щоб переконатися, що приклад працює в Python 2, я включив параметр. (Зауважте, що map()поводиться так, як zip_longest()у Python 2, тоді як він поводиться як zip()у Python 3.)
Sven Marnach

60

Документи явно пропонують, що це основне використання для itertools.repeat:

Зробіть ітератор, який повертає об’єкт знову і знову. Працює безстроково, якщо не вказано аргумент часу. Використовується як аргумент 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]

Це створює чудове ліниво-функціональне мовне рішення, яке також ідеально читається в термінах Python-iterator.


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

Не впевнений, що мав на увазі його питання, але я думаю, що я додаю значення 2, і ці 2 не є фіксованими, і вони повинні надходити від користувача, як arg1 йде. тож у цьому випадку як ми повинні викликати add (x, y) під картою?
Бімлеш Шарма

15

Правильна відповідь простіша, ніж ви думаєте. Просто робіть:

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

І змінити реалізацію add, щоб взяти кортеж, тобто

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

Func- це назва вашої оригінальної функції - для ОП тісно було б, addнаприклад,
Тодор Мінаков

6

Я вважаю, що вам потрібно:

from itertools import starmap


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

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

помилка у вашому прикладі: список (зірка (тест, [(1, 2, 3), (4, 5, 6)]])
Teebu

О, так. Дякую :)
Shqi.Ян

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.