Имеет ли Python какой-либо эквивалент цикла (не foreach)
Итераторы Python велики и все, но иногда мне действительно нужен цикл цикла C, а не цикл foreach. Например, у меня есть дата начала и дата окончания, и я хочу что-то делать каждый день в этом диапазоне. Я могу сделать это с помощью цикла while, конечно:
current = start
while current <= finish:
do_stuff(current)
current += timedelta(1)
Это работает, но это 3 строки вместо 1 (на языках C или C), и я часто забываю писать инкрементирующую строку, особенно если тело цикла довольно сложно. Есть ли более элегантный и менее подверженный ошибкам способ сделать это в Python?
Ответы
Ответ 1
Элегантный и Pythonic способ сделать это - инкапсулировать идею ряда дат в свой собственный генератор, а затем использовать этот генератор в вашем коде:
import datetime
def daterange(start, end, delta):
""" Just like `range`, but for dates! """
current = start
while current < end:
yield current
current += delta
start = datetime.datetime.now()
end = start + datetime.timedelta(days=20)
for d in daterange(start, end, datetime.timedelta(days=1)):
print d
печатает:
2009-12-22 20:12:41.245000
2009-12-23 20:12:41.245000
2009-12-24 20:12:41.245000
2009-12-25 20:12:41.245000
2009-12-26 20:12:41.245000
2009-12-27 20:12:41.245000
2009-12-28 20:12:41.245000
2009-12-29 20:12:41.245000
2009-12-30 20:12:41.245000
2009-12-31 20:12:41.245000
2010-01-01 20:12:41.245000
2010-01-02 20:12:41.245000
2010-01-03 20:12:41.245000
2010-01-04 20:12:41.245000
2010-01-05 20:12:41.245000
2010-01-06 20:12:41.245000
2010-01-07 20:12:41.245000
2010-01-08 20:12:41.245000
2010-01-09 20:12:41.245000
2010-01-10 20:12:41.245000
Это похоже на ответ о range
, за исключением того, что встроенный range
не будет работать с datetime, поэтому мы должны создать наш собственный, но по крайней мере мы можем это сделать только один раз в инкапсулированном путь.
Ответ 2
Выполняя это в компактном виде, это нелегко в Python, так как одна из основных концепций языка не может выполнять присвоения при сравнении.
Для чего-то сложного, как дата, я думаю, что ответ Неда велик, но для более простых случаев я нашел очень полезной функцию itertools.count(), которая возвращает последовательные числа.
>>> import itertools
>>> begin = 10
>>> end = 15
>>> for i in itertools.count(begin):
... print 'counting ', i
... if i > end:
... break
...
counting 10
counting 11
counting 12
counting 13
counting 14
counting 15
counting 16
Я нашел его менее подверженным ошибкам, так как легко, как вы сказали, забыть "current + = 1". Для меня кажется более естественным сделать бесконечный цикл, а затем проверить конечное условие.
Ответ 3
Это будет работать в крайнем случае:
def cfor(start, test_func, cycle_func):
"""A generator function that emulates the most common case of the C for
loop construct, where a variable is assigned a value at the begining, then
on each next cycle updated in some way, and exited when a condition
depending on that variable evaluates to false. This function yields what
the value would be at each iteration of the for loop.
Inputs:
start: the initial yielded value
test_func: called on the previous yielded value; if false, the
the generator raises StopIteration and the loop exits.
cycle_func: called on the previous yielded value, retuns the next
yielded value
Yields:
var: the value of the loop variable
An example:
for x in cfor(0.0, lambda x: x <= 3.0, lambda x: x + 1.0):
print x # Obviously, print(x) for Python 3
prints out
0.0
1.0
2.0
3.0
"""
var = start
while test_func(var):
yield var
var = cycle_func(var)
Ответ 4
Только для итерации вы должны использовать xrange по диапазону, так как xrange просто вернет итератор, тогда как диапазон создаст фактический объект списка, содержащий весь целочисленный диапазон от первого до последнего -1 (что, очевидно, менее эффективно, когда все, что вам нужно, это простой цикл):
for i in xrange(current,finish+1, timedelta(1)):
do_stuff(i)
Кроме того, имеется перечисление, которое возвращает объект перечисления, который даст инкрементный счетчик и значение коллекции, то есть:
l = ["a", "b", "c"]
for ii, value in enumerate(l):
print ii, value
Результат:
0 a
1 b
2 c