2009-12-29 5 views
33

Это отношение к следующим: (В Python кодекс)Когда следует использовать карту вместо цикла For?

for i in object: 
    doSomething(i) 

против

map(doSomething, object) 

Как легко понять, и короткий, но есть ли разница в скорости? Теперь, если doSomething имеет возвращаемое значение, нам нужно было проверить, что он будет возвращен как список с карты, а в цикле for мы могли бы либо создать наш собственный список, либо проверить по одному.

for i in object: 
    returnValue = doSomething(i) 
    doSomethingWithReturnValue(returnValue) 

против

returnValue = map(doSomething, object) 
map(doSomethingWithReturnValue, returnValue) 

Теперь, я чувствую, что два расходятся немного. Две функции doSomethingWithReturnValue могут отличаться в зависимости от того, проверяете ли они их на ходу, когда мы проходим через цикл или если проверка их всех сразу в конце дает разные результаты. Также кажется, что цикл for всегда будет работать, возможно, медленнее, когда карта будет работать только в определенных сценариях. Конечно, мы могли бы сделать извинения, чтобы сделать любую работу, но все дело в том, чтобы избежать такой работы.

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

Спасибо!

+2

Вы можете использовать функцию list/dictionary/set или генератор вместо карты - зависит от того, что делает doSomething(). –

+2

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

ответ

22

map полезен, если вы хотите применить функцию к каждому элементу итерации и вернуть список результатов. Это проще и более кратким, чем использование цикла for и построение списка.

for часто читается для других ситуаций, и в lisp было много конструкций итераций, которые были написаны в основном с использованием макросов и карт. Таким образом, в случаях, когда map не подходит, используйте петлю for.

Теоретически, если бы у нас был компилятор/интерпретатор, который был достаточно умен, чтобы использовать несколько процессоров/процессоров, то map может быть реализован быстрее, поскольку различные операции над каждым элементом могут выполняться параллельно. Однако я не думаю, что это так.

+0

PLINQ (C#) может это сделать. –

+12

Почему прошлое? Лисп жив и пинает. – Svante

+3

На самом деле 'map' превосходит' for' производительность даже в одном потоке, потому что цикл написан на C. См. Мой пост для теста скорости. – iamamac

7

просто используйте список понятий: они более питонические. Они также имеют синтаксис, аналогичный выражению генератора, который позволяет легко переключаться с одного на другой. Вам не нужно ничего менять при преобразовании кода в py3k: map возвращает итерируемый в py3k, и вам придется настроить свой код.

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

0

EDIT: Я не знал, что map равно itertools.imap после python 3.0. Таким образом, вывод здесь может быть неправильным. Я снова запустим тест на python 2.6 завтра и опубликую результат.

Если doSomething очень «крошечная», map может быть намного быстрее, чем for петли или список-понимания:

# Python 3.1.1 (r311:74483, Aug 17 2009, 17:02:12) [MSC v.1500 32 bit (Intel)] on win32 

from timeit import timeit 

do = lambda i: i+1 

def _for(): 
    for i in range(1000): 
    do(i) 

def _map(): 
    map(do, range(1000)) 

def _list(): 
    [do(i) for i in range(1000)] 

timeit(_for, number=10000) # 2.5515936921388516 
timeit(_map, number=10000) # 0.010167432629884843 
timeit(_list, number=10000) # 3.090125159839033 

Это потому, что map написан на С, в то время как for цикл и list-comprehension запускается в виртуальной машине python.

+1

Я не знаю, откуда вы получаете свои номера, но в моем случае (Python 2.6) цикл for работает быстрее, примерно на 5%. Ваш код даже не правильный, поскольку _list делает меньше итераций. Огромные различия, которые вы получили, указывают на то, что с вашей настройкой что-то серьезно не так. – interjay

+0

Это просто смешно. ваши коды вообще не эквивалентны. прочитайте мой ответ. даже без сопоставления с объектами по сравнению с дихотомией, ваш код отличается, это позор, что вы его не видите – SilentGhost

+0

Извините, есть некоторая опечатка, когда я скопировал код и сделал форматирование. Я запускаю python 3.1.1 на моем Core Duo 4300 PC, «карта» значительно превосходит две другие. – iamamac

10

Вы знакомы с модулем timeit? Ниже приведены некоторые тайминги. -s выполняет одноразовую настройку, а затем команда зацикливается и записывается самое лучшее время.

1> python -m timeit -s "L=[]; M=range(1000)" "for m in M: L.append(m*2)" 
1000 loops, best of 3: 432 usec per loop 

2> python -m timeit -s "M=range(1000);f=lambda x: x*2" "L=map(f,M)" 
1000 loops, best of 3: 449 usec per loop 

3> python -m timeit -s "M=range(1000);f=lambda x:x*2" "L=[f(m) for m in M]" 
1000 loops, best of 3: 483 usec per loop 

4> python -m timeit -s "L=[]; A=L.append; M=range(1000)" "for m in M: A(m*2)" 
1000 loops, best of 3: 287 usec per loop  

5> python -m timeit -s "M=range(1000)" "L=[m*2 for m in M]" 
1000 loops, best of 3: 174 usec per loop 

Обратите внимание, что все они аналогичны, за исключением последних двух. Это вызовы функции (L.append или f (x)), которые сильно влияют на время. В # 4 поиск L.append выполняется один раз в настройке. В №5 используется список comp без вызовов функций.

+0

Я думаю, вы имеете в виду мой пост. Да, я обнаружил серьезную проблему, что 'map' возвращает итераторы в py3k, но я не думаю, что есть что-то неправильное с' timeit', 'range' возвращает итераторы, поэтому незначительное влияние не ставится на этапе настройки. – iamamac

+0

> python3 -m timeit "[m for m in range (1000)]" 10000 циклов, наилучший из 3: 114 usec за цикл > python3 -m timeit -s M = список (диапазон (1000)) "[ m для m в M] " 10000 петель, наилучший из 3: 83 usec за цикл Существует существенная разница в создании списка только один раз. –

Смежные вопросы