2009-12-23 3 views
13

Python-Iteratoren sind groß und alle, aber manchmal möchte ich wirklich eine C-style for-Schleife - keine foreach-Schleife. Zum Beispiel habe ich ein Startdatum und ein Enddatum und ich möchte etwas für jeden Tag in diesem Bereich tun. Ich kann dies tun mit einer while-Schleife, natürlich:Hat Python irgendwelche For-Schleife-Äquivalent (nicht Foreach)

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

Dies funktioniert, aber es ist 3 Zeilen anstelle von 1 (in C oder C-basierten Sprachen) und ich finde mich oft zu vergessen die Inkrementierung Zeile zu schreiben, vor allem, wenn der Schleifenkörper sehr komplex ist. Gibt es einen eleganteren und weniger fehleranfälligen Weg, dies in Python zu tun?

Antwort

30

Der elegante und Pythonic Weg, es zu tun, ist die Idee einer Reihe von Daten in einem eigenen Generator zu verkapseln, dann diesen Generator in Ihrem Code verwenden:

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 

Drucke :

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 

Dies ist etwa range auf die Antwort ähnlich, mit der Ausnahme, dass die range nicht mit Datetimes arbeitet in-built, so haben wir um unser eigenes zu schaffen, aber zumindest können wir es nur einmal gekapselt machen.

+1

+1 nicht nur weil es die einzige Antwort ist, die ** tatsächlich funktioniert ** aber auch weil es der richtige ist. Ernsthaft, wähle keine Antworten, die einfach gut aussehen * –

-2

Um nur zu Iterieren, sollten Sie eigentlich xrange über den Bereich verwenden, da xrange einfach einen Iterator zurückgibt, während range ein tatsächliches Listenobjekt mit dem ganzen Ganzzahlbereich von first bis last-1 erzeugt effizient ist offensichtlich weniger, wenn alles, was Sie eine einfache for-Schleife ist wollen):

for i in xrange(current,finish+1, timedelta(1)): 
    do_stuff(i) 

Zusätzlich ist es aufzuzählen, die eine enumerate-Objekt zurückgibt, die eine Inkrementierung Zahl und den Wert einer Sammlung wird ergeben, das heißt:

l = ["a", "b", "c"] 
for ii, value in enumerate(l): 
    print ii, value 

Ergebnis:

0 a 
1 b 
2 c 
+2

-1 Test-Antworten, bevor Sie veröffentlichen. Ergebnis ist 'TypeError: eine Ganzzahl ist erforderlich'. Alle Argumente von 'xrange()' müssen Ganzzahlen sein. –

+0

'xrange' sollte" irange "heißen, da es einen Iterator zurückgibt, während' range' immer eine Liste zurückgeben sollte; Die einzige Einschränkung für 'xrange' sollte sein, dass' next = start; nächste = nächste + Schritt; bis next == end', dh dass 'start'' '_add__' zu' step' sein muss und das Ergebnis muss '__cmp__'able to' end' sein –

2

Wenn Sie es auf kompakte Weise tun, ist es in Python nicht einfach, da eines der Grundkonzepte der Sprache darin besteht, keine Zuordnungen für Vergleiche zu erstellen.

Für etwas komplexes, wie ein Datum, denke ich, dass die Antwort von Ned groß ist, aber für einfachere Fälle fand ich sehr nützlich die itertools.count() -Funktion, die fortlaufende Zahlen zurückgeben.

>>> 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 

Ich fand es weniger fehleranfällig, da es einfach ist, wie Sie, sagte der ‚Strom + = 1‘ zu vergessen. Für mich erscheint es natürlicher, eine Endlosschleife zu machen und dann nach einer Endbedingung zu suchen.

+5

WTF? Warum nicht einfach 'für i in xrange (Anfang, Ende) verwenden:'? –

1

Dies wird in eine Prise arbeiten:

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) 
Verwandte Themen