Идиоматический Python: цикл 'times'

Скажем, у меня есть функция foo, которую я хочу вызвать n раз. В Ruby я бы написал:

n.times { foo }

На Python я мог написать:

for _ in xrange(n): foo()

Но это кажется хакерским способом делать что-то.

Мой вопрос: есть ли идиоматический способ сделать это в Python?


person perimosocordiae    schedule 17.04.2010    source источник
comment
Не забывайте, что ruby ​​также вычисляет переменную счетчика для цикла. В частности, n раз {| i | foo}. В вашем случае вы просто отказываетесь от него. Так почему это нормально для Ruby, а для Python - хакерское?   -  person Andriy Drozdyuk    schedule 17.04.2010
comment
Потому что мне не нужно выбрасывать это явно. :-) Версия Ruby также лучше демонстрирует цель строки, IMO.   -  person perimosocordiae    schedule 17.04.2010
comment
Я использую x как переменную для выброса, но _ кажется более идиоматичным. И да, в python нет times функции ..   -  person hasen    schedule 17.04.2010
comment
@perimosocoriade, если вы беспокоитесь о том, чтобы ясно продемонстрировать свои намерения другим разработчикам python, _ - это то, что вам нужно.   -  person TM.    schedule 17.04.2010
comment
Хотя это как-то идиоматично, мне очень не нравится переменная _. Меня это сбивает с толку. Обычно я предпочитаю использовать i, но, может быть, потому, что раньше я много программировал на C ...   -  person Khelben    schedule 17.04.2010
comment
@Khelben: Использование значимых имен для выбрасывания переменных - плохая идея, ИМО. Это своего рода обещание, что эта переменная где-то будет использоваться, что может ввести в заблуждение.   -  person gorsky    schedule 17.04.2010
comment
for x in iterator так же просто, как и в Python. Я не могу придумать способ еще больше его упростить.   -  person jathanism    schedule 17.04.2010
comment
@drozzy одна из причин, по которой рубиновые блоки не применяют арность, заключается в том, что вы можете выбросить переменные точно так же, как в этом примере - это не повредит производительности, и это идиоматично. Ничего хакерского в этом нет.   -  person horseyguy    schedule 24.06.2013


Ответы (4)


Вопрос предполагает, что вызов foo () n раз является априори необходимостью. Откуда взялось? Это длина чего-то повторяемого? Затем выполните итерацию по итерации. Когда я беру в руки Python, я обнаружил, что использую мало или совсем не использую произвольные значения; за вашим n скрывается еще одно важное значение, которое потерялось, когда оно стало целым числом.

Сегодня я наткнулся на провокационную статью Никлауса Вирта для IEEE Computer под названием Хорошие идеи - в Зазеркалье (заархивировано version для будущих читателей). В разделе 4 он предлагает другой взгляд на программные конструкции, которые все (включая его самого) считали само собой разумеющимся, но которые содержат выразительные недостатки:

«Общность утверждения Алгола for должна была стать предупредительным сигналом для всех будущих дизайнеров, чтобы они всегда держали в уме главную цель конструкции и устали от преувеличенной общности и сложности, которые могут легко стать контрпродуктивными».

Алгол for эквивалентен C / Java for, он просто делает слишком много. Эту статью полезно читать хотя бы потому, что она заставляет не принимать как должное столько того, что мы с такой готовностью делаем. Так что, возможно, лучше спросить: «Зачем вам нужен цикл, который выполняется произвольное количество раз?»

person msw    schedule 17.04.2010
comment
Вы правы, случаев, когда это необходимо, очень мало. Хотя есть некоторые. В моем случае мне нужно было пропустить определенное количество строк в файле перед чтением данных. Кроме того, для кода тестирования обычно требуется произвольное количество прогонов. Вещи, которые влияют на какое-то глобальное состояние (например, операции ввода-вывода), являются наиболее частыми виновниками. - person perimosocordiae; 17.04.2010
comment
Если вы хотите пропустить заданное количество строк из файла, вы можете использовать: next (itertools.islice (thefile, n-1, n)) Это дает преимущество скорости, хотя ему не хватает ясности цикла for. - person Duncan; 17.04.2010
comment
Я проснулся, мечтая о мудрости написания class skipfile(file) с skipfile(path, skiplines=n) и skipfile.skip(lines=m), и я виню вас, перимосокордии;) - person msw; 17.04.2010
comment
К сожалению, эти ссылки IEEE теперь не работают. Статья доступна для подписчиков или для покупки на компьютере . org / portal / web / csdl / doi / 10.1109 / MC.2006.20. - person Sam Dutton; 28.11.2010
comment
@SamDutton К сожалению, эта ссылка тоже не работает - person Josh K; 29.12.2012
comment
@msw Есть ли у вас сохраненная копия PDF, на которую вы можете заменить ссылки? - person Josh K; 29.12.2012
comment
@perimosocordiae: вы можете пропустить n строк в файле, используя consume() рецепт itertools: next(islice(file, n, n), None) - person jfs; 08.03.2013
comment
@laidback - спасибо за исправление ссылки - person msw; 07.04.2013
comment
Я согласен с мнением, но я просто не понимаю, как это отвечает на вопрос ... - person jxstanford; 16.08.2014
comment
Устали? Я уже устал от этого. - person Aaron Hall; 05.08.2015
comment
Зачем вам нужен цикл, который выполняется произвольное количество раз? Вот почему мне это нужно: я приказываю компьютеру прочитать данные с прибора N раз. Я должен вызвать функцию read_data () это количество раз. Если есть способ выполнить подобные задачи, перебирая существующий массив, я все слышу! - person gradi3nt; 19.01.2016
comment
Я хочу нарисовать кривую Гильберта, поэтому я пишу lsystem, lsystem должна применять правила для каждой итерации. Я не знаю, сколько итераций я хочу сделать, достаточно, чтобы это было интересно, не так много, чтобы они уходили за край экрана. Я хочу попробовать 5 итераций и откорректировать их на глаз. - person Joshua Cheek; 05.01.2019
comment
Я ценю общее мнение, но это не совсем ответ на вопрос. Несмотря на то, что я тот, кто предпочитает FP и поэтому не любит итеративные циклы, я считаю, что метод времени Ruby очень прост и выразителен (что делает ваш комментарий вирта просто неправильным в качестве ответа) и подходит для императивного языка. Это объектно-ориентированный эквивалент нотации Чёрча для целых чисел в FP: для чего нужны числа в коде? За то, что сделал что-то N раз. - person itsbruce; 20.12.2020
comment
Python не может делать вещи Ruby Times, потому что у него нет блоков кода. Метафора времени Ruby на самом деле очень хороша для тех случаев, когда она действительно уместна. Это только один пример того, как отсутствие блоков кода снижает выразительность Python. - person itsbruce; 20.12.2020

Вы уже показали идиоматический путь:

for _ in range(n): # or xrange if you are on 2.X
    foo()

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

person TM.    schedule 17.04.2010
comment
Я считаю это хакерским только потому, что он генерирует значения диапазона, которые я выбрасываю с помощью соглашения _. Похоже, что я по какой-то причине ухожу в сторону. - person perimosocordiae; 17.04.2010
comment
Вот почему вы используете _ вместо другого имени. Это означает выбросить переменную - идея _ как выброса присуща Python, Prolog, Haskell и, предположительно, некоторым другим языкам. - person MatrixFrog; 17.04.2010
comment
Разве диапазон не является неэффективным, потому что он использует n байтов памяти для счетчика, тогда как цикл C for будет использовать только один байт для счетчика? - person bcoughlan; 02.09.2011
comment
+1 за использование _, не знал этого! - person Jacob Tomlinson; 06.01.2014
comment
Это должен быть принятый ответ. - person B Seven; 22.02.2019
comment
Само использование _ в Python немного хакерское. В некоторых других языках это синтаксическая конструкция, которая игнорирует любое связанное значение (и, таким образом, немедленно позволяет компилятору оптимизировать его, а сборщик мусора освобождает любой переданный ему объект). В Python это настоящая переменная, только с условием, что она не используется ни для чего другого в логике вашего приложения. Поскольку переменные-интеграторы цикла For-loop в Python сохраняются после завершения цикла, это особенно опасно. - person itsbruce; 21.12.2020

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

def times(self, n, *args, **kwargs):
    for _ in range(n):
        self.__call__(*args, **kwargs)

import new
def repeatable(func):
    func.times = new.instancemethod(times, func, func.__class__)
    return func

теперь добавьте @repeatable декоратор к любому методу, для которого вам нужен times метод:

@repeatable
def foo(bar):
    print bar

foo.times(4, "baz") #outputs 4 lines of "baz"
person Carson Myers    schedule 17.04.2010

Самый быстрый и чистый - itertools.repeat:

import itertools

for _ in itertools.repeat(None, n):
    foo()
person Alex Martelli    schedule 17.04.2010
comment
Это не избавляет от неиспользуемой переменной. - person dan04; 17.04.2010
comment
Я рассчитал пару методов, и метод itertools примерно на 50% быстрее, чем xrange. Хотя я бы не сказал, что он чище. - person perimosocordiae; 17.04.2010
comment
@ dan04, какая неиспользуемая переменная - _?! Пух-Леазе! -) - person Alex Martelli; 17.04.2010