Имеет ли Python какой-либо эквивалент цикла for (не foreach) - PullRequest
12 голосов
/ 23 декабря 2009

Итераторы Python великолепны и все такое, но иногда я действительно хочу использовать цикл в стиле C, а не цикл foreach. Например, у меня есть дата начала и дата окончания, и я хочу сделать что-то для каждого дня в этом диапазоне. Конечно, я могу сделать это с помощью цикла while:

    current = start
    while current <= finish:
        do_stuff(current)
        current += timedelta(1)

Это работает, но это 3 строки вместо 1 (в языках C или C), и я часто забываю писать увеличивающуюся строку, особенно если тело цикла довольно сложное. Есть ли более элегантный и менее подверженный ошибкам способ сделать это в Python?

Ответы [ 4 ]

29 голосов
/ 23 декабря 2009

Элегантный и 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 голосов
/ 23 декабря 2009

Делать это в компактном виде непросто в 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». Мне кажется более естественным сделать бесконечный цикл и затем проверить конечное условие.

1 голос
/ 10 сентября 2010

Это будет работать в крайнем случае:

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)
0 голосов
/ 23 декабря 2009

Только для итерации вы должны использовать xrange over range, поскольку xrange просто возвратит итератор, тогда как range создаст фактический объект списка, содержащий весь диапазон целых чисел от первого до последнего. 1 (что, очевидно, менее эффективно, когда все, что вам нужно, это простой цикл for):

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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...