Итераторы Python являются большими и все, но иногда я действительно хочу C-стиль для цикла - не цикл foreach. Например, у меня есть дата начала и дата окончания, и я хочу сделать что-то в течение каждого дня в том диапазоне. Я могу сделать это с некоторое время циклом, конечно:
current = start
while current <= finish:
do_stuff(current)
current += timedelta(1)
Это работает, но это - 3 строки вместо 1 (в C или языках на базе С), и я часто забываю для записи строки постепенного увеличения, особенно если тело цикла довольно сложно. Существует ли более изящное и меньше подверженного ошибкам способа сделать это в Python?
Элегантный и питонический способ сделать это - инкапсулировать идею диапазона дат в свой собственный генератор, а затем использовать этот генератор в своем коде:
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
Это похоже на ответ о диапазоне
, за исключением того, что встроенный диапазон
не будет работать с датами, поэтому мы должны создать свой собственный, но, по крайней мере, мы можем сделать это только один раз в инкапсулированном виде.
Только ради итерации, на самом деле следует использовать xrange за пределами диапазона, так как xrange просто вернет итератор, в то время как диапазон создаст реальный объект списка, содержащий весь целочисленный диапазон от первого до последнего-1 (что, очевидно, менее эффективно, когда все, что вам нужно, это простой цикл):
for i in xrange(current,finish+1, timedelta(1)):
do_stuff(i)
Кроме того, есть перечисление, которое возвращает объект перечисления, который будет давать инкрементирующий счетчик и значение коллекции, i. e.:
l = ["a", "b", "c"]
for ii, value in enumerate(l):
print ii, value
Результат:
0 a
1 b
2 c
Сделать это компактно на 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
Я нашел ее менее склонной к ошибкам, так как легко, как вы сказали, забыть "текущий += 1". Мне кажется более естественным сделать бесконечный цикл, а затем проверить условие завершения.