2.12. Bucle

O buclă rulează în mod repetat același bloc de cod. Python are două forme: while, care continuă atât timp cât o condiție rămâne adevărată, și for, care parcurge elementele unei secvențe.

Diagrame de flux comparative. while: testează condiția, rulează corpul dacă este adevărată, repetă. for: ia următorul element din iterabil, rulează corpul, repetă până la epuizare.

while testează în continuare o condiție; for parcurge o secvență până la epuizarea acesteia.

2.12.1. Bucle while

O buclă while își testează condiția înainte de fiecare iterație și rulează corpul până când testul devine fals:

count = 0
while count < 5:
    print(count)
    count += 1

Rezultat:

0
1
2
3
4

Dacă condiția este adevărată la început și nu devine niciodată falsă, bucla rulează la nesfârșit. while True: este idiomul standard pentru o buclă principală, din care se iese explicit cu break:

while True:
    step()
    if done():
        break

2.12.2. Bucle for

O buclă for parcurge elementele unui iterabil – o listă, un tuplu, un șir, octeți, un dicționar sau orice altceva care suportă iterarea:

for fruit in ["apple", "banana", "cherry"]:
    print(fruit)

Rezultat:

apple
banana
cherry

Aceeași formă funcționează pe un șir, unde fiecare element este un șir de un singur caracter:

for letter in "OpenMV":
    print(letter)

Rezultat:

O
p
e
n
M
V

Iterarea directă a unui dicționar produce cheile acestuia, în ordinea inserării:

for key in {"a": 1, "b": 2}:
    print(key)

Rezultat:

a
b

Fiecare trecere leagă variabila buclei (fruit, letter, key) de următorul element. După încheierea buclei, variabila păstrează valoarea din ultima iterație.

2.12.3. range

Pentru bucle peste un interval numeric, folosește range():

  • range(stop) – 0, 1, …, stop - 1.

  • range(start, stop) – start, start + 1, …, stop - 1.

  • range(start, stop, step) – cu un pas personalizat (valorile negative numără descrescător).

for i in range(5):           # 0, 1, 2, 3, 4
    print(i)

for i in range(2, 8, 2):     # 2, 4, 6
    print(i)

for i in range(10, 0, -1):   # 10, 9, ..., 1
    print(i)

range() produce valori în mod leneș (lazy) – nu construiește o listă în memorie. Pentru a obține o list propriu-zisă, încadreaz-o: list(range(10)).

2.12.4. enumerate

Când bucla are nevoie atât de index, cât și de element, enumerate() produce perechi (index, item):

for i, name in enumerate(["a", "b", "c"]):
    print(i, name)
# 0 a
# 1 b
# 2 c

Pornește indexul de la altă valoare decât zero trimițând un al doilea argument: enumerate(items, start=1).

2.12.5. zip

Pentru a parcurge două (sau mai multe) iterabile în pas sincron, folosește zip(). Acesta produce un tuplu pentru fiecare poziție și se oprește la cel mai scurt input:

names  = ["alice", "bob", "carol"]
scores = [88, 92, 70]

for name, score in zip(names, scores):
    print(name, score)

Rezultat:

alice 88
bob 92
carol 70

2.12.6. Atribuire în linie cu :=

Operatorul walrus := este o atribuire care este totodată și o expresie. Leagă un nume și se evaluează la aceeași valoare în același timp. Într-o buclă while, acesta condensează tiparul comun „citește, verifică, corp” într-o singură linie:

# without walrus
value = next_value()
while value is not None:
    process(value)
    value = next_value()

# with walrus
while (value := next_value()) is not None:
    process(value)

Cele două forme fac același lucru. Apelează la := atunci când duplicarea atribuirii afectează cu adevărat lizibilitatea; nu apela la el doar pentru a fi inventiv. Parantezele sunt obligatorii în majoritatea pozițiilor pentru a menține expresia neambiguă.