pythonic способ сделать что-то N раз без переменной индекса?

С каждым днем ​​я люблю питона все больше и больше.

Сегодня я писал такой код:

for i in xrange(N):
    do_something()

Мне пришлось что-то сделать N раз. Но каждый раз не зависел от значения i (индексная переменная). Я понял, что создавал переменную, которую никогда не использовал (i), и подумал: «Конечно, есть более питонический способ сделать это без необходимости в этой бесполезной индексной переменной».

Итак ... вопрос в том, знаете ли вы, как выполнить эту простую задачу более (питонически) красивым способом?


person Manuel Araoz    schedule 04.06.2010    source источник
comment
Я только что узнал о переменной _, но в противном случае я бы подумал о том, как вы это делаете, Pythonic. Я не думаю, что когда-либо видел простой цикл for, выполняемый каким-либо другим способом, по крайней мере, на python. Хотя я уверен, что есть определенные варианты использования, когда вы смотрите на это и говорите «Подождите», это выглядит ужасно, но в целом xrange является предпочтительным способом (насколько я видел).   -  person Wayne Werner    schedule 04.06.2010
comment
Возможный дубликат Is Можно ли реализовать цикл Python для диапазона без переменной итератора?   -  person Ciro Santilli 新疆再教育营六四事件ۍ    schedule 25.11.2015
comment
ПРИМЕЧАНИЕ: xrange не существует в Python3. Вместо этого используйте range.   -  person John Henckel    schedule 07.12.2016


Ответы (8)


Немного более быстрый подход, чем цикл на xrange(N):

import itertools

for _ in itertools.repeat(None, N):
    do_something()
person Alex Martelli    schedule 04.06.2010
comment
Насколько быстрее? Есть ли разница в Python 3.1? - person Hamish Grubijan; 04.06.2010
comment
@Hamish: Мой тест с 2.6 говорит, что на 32% быстрее (23,2 мкс против 17,6 мкс для N = 1000). Но в любом случае сейчас самое время. Я бы по умолчанию использовал код OP, потому что он более удобочитаемый (для меня). - person Mike Boers; 04.06.2010
comment
Приятно знать о скорости. Я, безусловно, разделяю мнение Майка о том, что код OP более читабелен. - person Wayne Werner; 04.06.2010
comment
@Wayne, я думаю, привычка действительно очень сильна - за исключением того факта, что вы к ней привыкли, зачем еще считать от 0 до N-1 [[и полностью игнорировать счет]] каждый раз, выполняя этот счет- Независимая операция по своей сути может быть более ясной, чем повторение N раз следующей операции ...? - person Alex Martelli; 04.06.2010
comment
ты уверен, что скорость действительно актуальна? Разве не так, что если вы сделаете что-нибудь существенное в этом цикле, это, скорее всего, займет в сотни или тысячи раз столько же времени, сколько и выбранный вами стиль итерации? - person Henning; 05.05.2014
comment
Кроме того, да, это как-то яснее, поскольку он использует метод с именем repeat, чтобы что-то повторить. Но это также более длинное и более редкое выражение, а также добавляет дополнительный импорт, который также съедает время и ресурсы. Я предполагаю, что это на самом деле очень философское решение - в основном причина, по которой я приземлился здесь в поисках альтернативы вышеупомянутому после того, как просто написал, заключается в фрагменте кода. Тем не менее, я не на 100% убежден, что нужно идти и менять код сейчас ... - person Henning; 05.05.2014
comment
Это полностью упускает суть, потому что вы используете цикл for, а синтаксис циклов требует коллекции и переменной, которая будет содержать по одному элементу этой коллекции за раз. Но, как сказал OP, ему не нужен этот элемент, и поэтому нет смысла использовать цикл for. TL; DR: мы хотим избавиться от i, _ и т. Д. - person ychaouche; 29.07.2014

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

# A long way to do integer exponentiation
num = 2
power = 3
product = 1
for _ in xrange(power):
    product *= num
print product
person GreenMatt    schedule 04.06.2010
comment
Не тот, кто проголосовал против, но это может быть потому, что вы ссылаетесь на другой пост вместо того, чтобы добавлять подробности в ответ. - person Downgoat; 24.01.2016
comment
@Downgoat: Спасибо за отзыв. Тем не менее, об этой идиоме особо нечего сказать. Ссылаясь на другой пост, я хотел указать, что поиск мог дать ответ. Мне кажется странным, что этот вопрос получил в несколько раз больше голосов, чем другой. - person GreenMatt; 12.04.2016

Я просто использую for _ in range(n), сразу по делу. Он сгенерирует весь список огромных чисел в Python 2, но если вы используете Python 3, это не проблема.

person L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o&#x    schedule 04.06.2010

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

def repeat(f, N):
    for _ in itertools.repeat(None, N): f()

тогда вы можете передать функцию в качестве аргумента.

person Anycorn    schedule 04.06.2010
comment
@ Хамиш: Почти ничего. (17,8 мкс на цикл при тех же условиях, что и время ответа Алекса, для разницы в 0,2 мкс). - person Mike Boers; 04.06.2010

_ - это то же самое, что и x. Однако это идиома Python, которая используется для обозначения идентификатора, который вы не собираетесь использовать. В python эти идентификаторы не запоминают и не выделяют место, как переменные в других языках. Об этом легко забыть. Это просто имена, указывающие на объекты, в данном случае целое число на каждой итерации.

person Khorkrak    schedule 04.06.2010

Я нашел различные ответы действительно элегантными (особенно ответы Алекса Мартелли), но я хотел оценить производительность из первых рук, поэтому приготовил следующий сценарий:

from itertools import repeat
N = 10000000

def payload(a):
    pass

def standard(N):
    for x in range(N):
        payload(None)

def underscore(N):
    for _ in range(N):
        payload(None)

def loopiter(N):
    for _ in repeat(None, N):
        payload(None)

def loopiter2(N):
    for _ in map(payload, repeat(None, N)):
        pass

if __name__ == '__main__':
    import timeit
    print("standard: ",timeit.timeit("standard({})".format(N),
        setup="from __main__ import standard", number=1))
    print("underscore: ",timeit.timeit("underscore({})".format(N),
        setup="from __main__ import underscore", number=1))
    print("loopiter: ",timeit.timeit("loopiter({})".format(N),
        setup="from __main__ import loopiter", number=1))
    print("loopiter2: ",timeit.timeit("loopiter2({})".format(N),
        setup="from __main__ import loopiter2", number=1))

Я также придумал альтернативное решение, основанное на решении Мартелли и использующее map() для вызова функции полезной нагрузки. Хорошо, я немного обманул, позволив полезной нагрузке принимать параметр, который отбрасывается: я не знаю, есть ли способ обойти это. Тем не менее, вот результаты:

standard:  0.8398549720004667
underscore:  0.8413165839992871
loopiter:  0.7110594899968419
loopiter2:  0.5891903560004721

Таким образом, использование map дает улучшение примерно на 30% по сравнению со стандартным циклом for и на 19% больше, чем у Мартелли.

person japs    schedule 12.04.2018

Предположим, вы определили do_something как функцию и хотите выполнить ее N раз. Может быть, вы можете попробовать следующее:

todos = [do_something] * N  
for doit in todos:  
    doit()
person Cox Chen    schedule 04.06.2010
comment
Конечно. Давайте не просто вызовем функцию миллион раз, давайте также выделим список из миллиона элементов. Если процессор работает, не следует ли немного нагружать память? Ответ нельзя охарактеризовать как однозначно «бесполезный» (он демонстрирует другой функциональный подход), поэтому я не могу проголосовать против, но я не согласен и полностью против. - person tzot; 06.06.2010
comment
Разве это не просто список из N ссылок на одно и то же значение функции? - person Nick McCurdy; 21.03.2016
comment
лучше сделать fn() for fn in itertools.repeat(do_something, N) и сохранить предварительную генерацию массива ... это моя предпочтительная идиома. - person F1Rumors; 10.05.2016
comment
@tzot Почему снисходительный тон? Этот человек приложил усилия к тому, чтобы написать ответ, и теперь, возможно, его отговорили от участия в будущем. Даже если это влияет на производительность, это рабочий вариант, и особенно, если N мало, последствия для производительности / памяти не существенны. - person davidscolgan; 02.10.2018
comment
Меня всегда удивляет, насколько одержимы производительностью разработчики Python :) Хотя я согласен с тем, что это не идиоматика, и кто-то новичок в Python, читающий его, может не понять, что происходит, так же ясно, как при простом использовании итератора - person Asfand Qazi; 04.01.2019

А как насчет простого цикла while?

while times > 0:
    do_something()
    times -= 1

У вас уже есть переменная; почему бы не использовать это?

person Carlos Ramirez    schedule 02.02.2012
comment
Я думаю только о том, что это 3 строки кода вместо одной (?) - person AJP; 10.03.2013
comment
@AJP - больше похоже на 4 строки против 2 строк - person ArtOfWarfare; 30.10.2013
comment
добавляет сравнение (раз> 0) и декремент (раз - = 1) к накладным расходам ... так медленнее, чем цикл for ... - person F1Rumors; 10.05.2016
comment
@ F1Rumors Не измерял, но я был бы удивлен, если бы JIT-компиляторы, такие как PyPy, генерировали более медленный код для такого простого цикла while. - person Philipp Claßen; 13.04.2018