Loops
A loop repeats a block of code multiple times.
| Loop type | Syntax | Use it for |
|---|---|---|
for |
|
Goes through an iterable (something that contains multiple values) one value at a time, assigning each value to loop_variable as it goes.
|
while |
|
A while loop repeats as long as a condition is True, checking the condition before it starts each pass.
|
Which loop do I need?
%%{init: {"themeVariables": {"edgeLabelBackground": "transparent"}}}%%
flowchart TD
choice{"Do you know what you're\nworking with, or how many\ntimes to repeat?"} -->|Yes| forConfirm["for loop!"]
forConfirm --> known{"Already have a collection\nto go through? (list, tuple,\ndict, set, or string)"}
choice -->|No, just know\nwhen to stop| whileConfirm["while loop!"]
whileConfirm --> whileloop["condition = a boolean expression"]
known -->|Yes| forvalue["iterable = the collection"]
known -->|No| forcount["iterable = range()"]
linkStyle 0 stroke:#3f6b52,stroke-width:2px
linkStyle 2 stroke:#3f6b52,stroke-width:2px
linkStyle 4 stroke:#3f6b52,stroke-width:2px
linkStyle 5 stroke:#a33f3f,stroke-width:2px
classDef decision fill:none,stroke:#8A8370,stroke-width:1px
classDef result fill:none,stroke:#3f6b52,stroke-width:2px,color:#3f6b52
classDef confirm fill:none,stroke:#3f6b52,stroke-width:3px,color:#3f6b52
class known,choice decision
class forvalue,forcount,whileloop result
class forConfirm,whileConfirm confirm
Fig. 7a — choosing between a for loop and a while loop
For loops
A for loop goes through an iterable (something that contains multiple values) one value at a time, assigning each value to loop_variable as it goes. They types of iterables are:
| Iterable | Loop variable | Use it for |
|---|---|---|
range() |
represents the current count | Repeating a block of code a set number of times |
| a Collection: list / tuple / dict / set / string | represents the current item | Repeating a block of code for each item in a collection |
See common patterns for accumulating something new during a loop, or counting, as you loop.
You can use control flow statements to break a loop early or continue ahead to the next iteration as needed.
Loop a certain number of times
iterable = range()
range()generates a sequence of numbers to loop over.- it will run the block of code once for each number in the sequence
- The first time it runs, the loop_variable will be equal to the first number in the range sequence, and so on until the block has run as many times are there are numbers in the range sequence
The 3 parts of range():
| range() part | Default value | Meaning |
|---|---|---|
start |
0 |
Where to start counting
|
stop |
(required — no default) | Where to stop (exclusive)
|
step |
1 |
How much to count by to get to the next number
|
How many parts you specify
| # of range parts given | Sets parts | Not set, so uses defaults for |
|---|---|---|
| 1 | stop |
start = 0, step = 1 |
| 2 | start, stop |
step = 1 |
| 3 | start, stop, step |
- |
range(stop)
for i in range(5):
print(i)
%%{init: {"themeVariables": {"edgeLabelBackground": "transparent"}}}%%
flowchart LR
a(("0")) -->|"+1"| b(("1")) -->|"+1"| c(("2")) -->|"+1"| d(("3")) -->|"+1"| e(("4"))
e -.->|"+1"| f(("5"))
classDef included fill:none,stroke:#3f6b52,stroke-width:2px,color:#3f6b52
classDef excluded fill:none,stroke:#8A8370,stroke-width:1px,stroke-dasharray:3 3,color:#8A8370
class a,b,c,d,e included
class f excluded
Fig. 7b — range(stop): start default is 0, step default is 1
range(start, stop)
for i in range(2, 6):
print(i)
%%{init: {"themeVariables": {"edgeLabelBackground": "transparent"}}}%%
flowchart LR
a(("2")) -->|"+1"| b(("3")) -->|"+1"| c(("4")) -->|"+1"| d(("5"))
d -.->|"+1"| e(("6"))
classDef included fill:none,stroke:#3f6b52,stroke-width:2px,color:#3f6b52
classDef excluded fill:none,stroke:#8A8370,stroke-width:1px,stroke-dasharray:3 3,color:#8A8370
class a,b,c,d included
class e excluded
Fig. 7c — range(2, 6): start is inclusive, stop is exclusive
range(start, stop, step)
for i in range(2, 8, 2):
print(i)
%%{init: {"themeVariables": {"edgeLabelBackground": "transparent"}}}%%
flowchart LR
a(("2")) -->|"+2"| b(("4")) -->|"+2"| c(("6"))
c -.->|"+2"| d(("8"))
classDef included fill:none,stroke:#3f6b52,stroke-width:2px,color:#3f6b52
classDef excluded fill:none,stroke:#8A8370,stroke-width:1px,stroke-dasharray:3 3,color:#8A8370
class a,b,c included
class d excluded
Fig. 7d — range(2, 8, 2): stepping by 2
Counting backwards
If start is larger than stop, use a negative step to count backwards instead.
for i in range(5, 0, -1):
print(i)
%%{init: {"themeVariables": {"edgeLabelBackground": "transparent"}}}%%
flowchart LR
a(("5")) -->|"-1"| b(("4")) -->|"-1"| c(("3")) -->|"-1"| d(("2")) -->|"-1"| e(("1"))
e -.->|"-1"| f(("0"))
classDef included fill:none,stroke:#3f6b52,stroke-width:2px,color:#3f6b52
classDef excluded fill:none,stroke:#8A8370,stroke-width:1px,stroke-dasharray:3 3,color:#8A8370
class a,b,c,d,e included
class f excluded
Fig. 7e — range(5, 0, -1): counting backwards with a negative step
Loop variable = an index
Naming the variable in a range() loop comes down to one of three choices:
-
i, for a simple counterShort for index — a naming convention borrowed from math, where
i,j, andkare the traditional names for a counting variable. It's not a special keyword; any name works, butiis what most Python code uses by convention for a loop overrange().for i in range(5): print(i) -
A descriptive name, when the count means something
If what you're counting through actually represents something, a descriptive name reads better than
i— says what the number means at a glance, instead of leaving the reader to infer it from how it's used. Same naming rule as any other variable:iis fine for a short, throwaway loop, but a meaningful name is worth it once the number stands for something specific.for year in range(2020, 2026): print(year) for attempt in range(3): print(attempt) -
_, when you don't need it at allUse
_instead of a real loop variable when you just need to repeat something a fixed number of times and don't need the number itself.for _ in range(3): print("hiss")
Loop through a collection
iterable = collection
A for loop steps through any type of collection1 the same way — the difference is what each pass hands you to work with.
How to loop each type
Each pass hands you the item itself, not its position. This is what sets a Python for loop apart from the index-counting loops in some other languages. A list is the most common thing to loop over, since it's Python's all-purpose ordered collection.
species = ["burmese", "rock", "ball", "blood"]
for s in species:
print(s)
Works exactly like looping over a list — a tuple just can't be changed once it's created. Anything you'd loop through in a list, you can loop through the same way in a tuple.
constrictors = ("ball", "burmese", "boa")
for s in constrictors:
print(s)
Looping directly over a dict gives you its keys, one at a time. Use .values() to get just the values instead, or .items() to get the key and value together — usually the most useful of the three.
snake = {"species": "ball", "length_ft": 5, "venomous": False}
for key, value in snake.items():
print(key, value)
for key in snake:
print(key)
for value in snake.values():
print(value)
Hands you each character in turn, in order — including spaces. A string is just a sequence of characters, so a for loop treats it the same way it treats a list or tuple.
name = "burmese python"
for letter in name:
print(letter)
Works the same as a list, except the order isn't guaranteed — a set has no fixed position for its items, so each pass just hands you the next value in whatever order Python happens to iterate.
species = {"burmese", "rock", "ball", "blood"}
for s in species:
print(s)
Loop variable = singular item
Looping over a collection follows a different naming convention than counting with range(): name the loop variable the singular of the collection's plural name — for snake in snakes:, for length in lengths: — so each pass reads as "this one item from the group." Site examples on this page often abbreviate to a single letter (s for species) to keep code blocks compact, but a real singular word is clearer in actual code.
snakes = ["burmese", "rock", "ball", "blood"]
for snake in snakes:
print(snake)
Loop with index and value
enumerate() hands you both the index and the value on every pass. It's the usual alternative to looping over range(len(species)) when you need the index but still want direct access to each item.
species = ["burmese", "rock", "ball", "blood"]
for i, s in enumerate(species):
print(i, s)
Loop in reverse
reversed() steps through a collection back to front, without needing to build a reversed copy first. Works on anything with a fixed order — list, tuple, string, range() — but not on a set, since it has no order to reverse.
species = ["burmese", "rock", "ball", "blood"]
for s in reversed(species):
print(s) # blood ball rock burmese
Going further
Loop two collections at the same time with zip()
zip() pairs up items from two (or more) iterables by position — the first item from each, then the second from each, and so on — stopping as soon as the shortest one runs out. Works with any iterable, mixed types included — list, tuple, string, dict (its keys, by default), even a range(). Because it is based on order, using an unordered collection like set or plain dict can produce pairings in an unpredictable order.
species = ["burmese", "rock", "ball", "blood"]
length_ft = [12, 4, 5, 3.5]
for s, ft in zip(species, length_ft):
print(s, ft)
List comprehensions: a one-line for loop
A list comprehension builds a new list by running an expression once per item — the same result as a for loop that appends to an empty list, written on a single line.
species = ["burmese", "rock", "ball", "blood"]
lengths = []
for s in species:
lengths.append(len(s))
print(lengths)
lengths = [len(s) for s in species]
print(lengths)
Add an if at the end to only keep items that match a condition:
species = ["burmese", "rock", "ball", "blood"]
print([s for s in species if s == "ball"])
Readable for a short, simple transformation — once the logic doesn't fit comfortably on one line, a regular for loop is usually clearer.
While loops
A while loop repeats its body for as long as a condition stays True, checked again before every pass — the right tool when you don't know ahead of time how many passes you'll need, unlike a for loop's fixed number of items. That condition can be any boolean expression, watching for something to happen rather than counting toward it.
handled = False
while not handled:
print("checking on the snake")
handled = True
Using a flag
A flag is a boolean variable, starting True or False, that gets flipped when something happens — used as the condition to end the loop based on an event rather than a pass count.
species = ["burmese", "rock", "ball", "blood"]
found = False
i = 0
while not found:
if species[i] == "ball":
found = True # flips the flag — the next check ends the loop
i += 1
print(found) # True
print(i) # 3 — stopped as soon as "ball" was found
Sentinel
A sentinel is a specific stop-value you watch for, rather than a plain True/False flag — the loop keeps running until it sees that exact value. A common use is reading input until the user signals they're done.
species = ""
while species != "quit":
species = input("Log a species (or 'quit' to stop): ")
if species != "quit":
print(f"logged: {species}")
Counter and flag names
A while loop doesn't create a loop variable automatically the way for does — whatever's driving the condition is a variable you declare and update yourself, so naming it clearly matters just as much.
- A counter — named the same way as a
forloop's:countworks generically, but a descriptive name (attempts,retries) reads better once the number means something specific. - A flag — named so
while not flag_name:reads like plain English —found,done,handled— rather than something that needs mental negation to parse.
handled = False
while not handled:
handled = True
Avoiding infinite loops
A while loop repeats forever unless something inside it moves the condition toward False. Nothing stops it automatically, and it freezes whatever's running it — always make sure something inside the loop body moves it toward ending, like incrementing a counter or updating the value being checked.
count = 0
while count < 3:
print(count)
count += 1 # forgetting this line means count < 3 is always True
Run a while loop example
All the examples above, combined into one script:
handled = False
while not handled:
print("checking on the snake")
handled = True
species = ["burmese", "rock", "ball", "blood"]
found = False
i = 0
while not found:
if species[i] == "ball":
found = True
i += 1
print(found)
print(i)
Boolean expressions
A boolean expression is needed for every while condition.
while [boolean expression]:
[indented code block that runs, and keeps running, as long as the expression stays True]
A boolean expression is a boolean value (True or False) or anything that produces one, and is treated as the condition that must be True in order to run a block of code.
A comparison looks different depending on the type of value being checked, as shown below. All of these comparisons result in a True or False boolean expression.
Comparisons by type
| Operator | Meaning |
|---|---|
== |
equal to |
!= |
not equal to |
> |
greater than |
< |
less than |
>= |
greater than or equal to |
<= |
less than or equal to |
length = 12
if length == 12: # equal to
print("exactly 12 ft")
if length != 4: # not equal
print("not 4 ft")
if length > 10: # greater than
print("long snake")
if length < 20: # less than
print("under 20 ft")
if length >= 12: # greater than or equal to
print("at least 12 ft")
if length <= 12.5: # less than or equal to
print("12.5 ft or shorter")
| Operator | Meaning |
|---|---|
== |
equal to |
!= |
not equal to |
in |
is a substring |
not in |
is not a substring |
> |
comes after alphabetically |
< |
comes before alphabetically |
name = "burmese python"
if name == "burmese python": # equal to
print("it's a burmese")
if name != "ball python": # not equal
print("not a ball python")
if "python" in name: # is it a substring
print("name contains 'python'")
if "anaconda" not in name: # is it not a substring
print("name doesn't mention anaconda")
if name > "ball python": # alphabetical comparison
print("comes after 'ball python' alphabetically")
| Check | Meaning |
|---|---|
[bool] |
is the bool True |
not [bool] |
is the bool False |
Don't compare booleans with == True or is True — a boolean is already the condition, so just use the value directly (or not the value).
venomous = False
if venomous: # is it True — don't write venomous == True
print("handle with care")
if not venomous: # is it False — don't write venomous == False
print("safe to handle")
| Operator | Meaning |
|---|---|
is |
is None |
is not |
is not None |
age = None
if age is None: # is it None
print("age not recorded")
if age is not None: # is it anything else
print("age was recorded")
| Operator | Meaning |
|---|---|
in |
value exists in the list |
not in |
value is missing from the list |
== |
same contents, in the same order |
!= |
different contents |
Or compare a specific item directly, like species[0] == "ball".
species = ["ball", "burmese", "boa"]
other_species = ["ball", "burmese", "boa"]
if "ball" in species: # is the value in the list
print("ball python is in the list")
if "anaconda" not in species: # is the value missing from the list
print("anaconda isn't in the list")
if species == other_species: # is it the same contents, in the same order
print("both lists match")
if "ball" == species[0]: # compare a specific item
print("ball python is the first item")
| Operator | Meaning |
|---|---|
== |
same contents, even if it's a different object |
is |
the exact same object, not just an equal one |
snake = ["ball", "burmese"]
other_snake = ["ball", "burmese"] # separate list, but equal contents
if snake == other_snake: # do they contain the same items?
print("equal contents")
if snake != ["ball"]: # different contents
print("not equal to a single-item list")
same_snake = snake # another name for `snake`
if snake is same_snake: # same_snake and snake point to the exact same list
print("this really is the same list")
if snake is not other_snake: # it's a different list, even though contents match
print("but not the same list")
| Operator | Meaning |
|---|---|
in |
value exists in the tuple |
not in |
value is missing from the tuple |
== |
same contents, in the same order |
!= |
different contents |
Or compare a specific item directly, like snake[0] == "ball".
snake = ("ball", "5ft", "not venomous")
other_snake = ("ball", "5ft", "not venomous")
if "ball" in snake:
print("species ball is in the tuple")
if snake == other_snake:
print("tuples match")
if "ball" == snake[0]:
print("ball python is the first item")
| Operator | Meaning |
|---|---|
== |
same contents, even if it's a different object |
is |
the exact same object, not just an equal one |
snake = ("ball", "burmese")
other_snake = ("ball", "burmese") # separate tuple, but equal contents
if snake == other_snake: # do they contain the same items?
print("equal contents")
same_snake = snake # another name for `snake`
if snake is same_snake: # same_snake and snake point to the exact same tuple
print("this really is the same tuple")
if snake is not other_snake: # it's a different tuple, even though contents match
print("but not the same tuple")
| Operator | Meaning |
|---|---|
in |
key exists |
not in |
key is missing |
Or compare a specific value directly, like snake["length"] > 2.
snake = {"species": "ball", "length": 3, "venomous": False}
if "venomous" in snake: # is it a key
print("snake dict tracks venomous status")
if "habitat" not in snake: # is it not a key
print("snake dict has no habitat key")
if snake["length"] > 2: # compare a specific value
print("snake in dict is over 2 ft")
Logical operators
Logical operators not, and, or let a single while condition combine boolean expressions to create more complex conditions.
not venomous # not False → True
length > 10 and venomous # True and False → False
length > 10 or venomous # True or False → True
A and B here are boolean expressions.
A |
B |
not A — flips to the opposite |
A and B — True only if both are True |
A or B — True if either is True |
|---|---|---|---|---|
| True | True | False | True | True |
| True | False | False | False | True |
| False | True | True | False | True |
| False | False | True | False | False |
Order of operations: When several logical operators appear together, Python evaluates not first, then and, then or. Even when parentheses aren't required, they often make the condition much easier to read.
Common patterns
A few variable patterns show up across both for and while loops, tracking something as the loop runs rather than controlling it directly.
Accumulator
An accumulator builds up a result across passes — summing, concatenating, or collecting values — instead of just tracking whether or how many times the loop has run. Initialize it before the loop, then update it inside the body each pass.
lengths_ft = [4.5, 12, 5, 3.5]
total = 0
for length in lengths_ft:
total += length
print(total) # 25.0
i = 0
total = 0
while i < len(lengths_ft):
total += lengths_ft[i]
i += 1
print(total) # 25.0
Accumulating into a list
Same pattern, just appending instead of adding — this is exactly what a list comprehension collapses into one line.
species = ["burmese", "rock", "ball", "blood"]
results = []
for s in species:
results.append(s.upper())
print(results) # ["BURMESE", "ROCK", "BALL", "BLOOD"]
Counter
A counter tracks how many times a loop has run, or how many items met some condition — counting up or down, instead of accumulating a result. It follows the same three steps as an accumulator: initialize it before the loop, check or use it, and update it inside the body.
species = ["ball", "burmese", "ball", "boa", "ball"]
count = 0
for s in species:
if s == "ball":
count += 1
print(count) # 3 — counts every "ball" in the list
attempts = 3
while attempts > 0:
print(attempts)
attempts -= 1
print("out of attempts")
Nested loops
A loop can contain another loop — any combination of for and while works, not just two of the same kind. Useful when each item in the outer collection has its own inner collection to go through, like a list of lists. The inner loop runs all the way through for every single pass of the outer one.
species_tags = {
"ball": ["docile", "captive-bred"],
"burmese": ["large", "escape-risk"],
}
for species, tags in species_tags.items():
for tag in tags:
print(species, tag)
Going further
Run a common patterns example
All the examples above, combined into one script:
lengths_ft = [4.5, 12, 5, 3.5]
total = 0
for length in lengths_ft:
total += length
print(total)
i = 0
total = 0
while i < len(lengths_ft):
total += lengths_ft[i]
i += 1
print(total)
species = ["burmese", "rock", "ball", "blood"]
results = []
for s in species:
results.append(s.upper())
print(results)
species = ["ball", "burmese", "ball", "boa", "ball"]
count = 0
for s in species:
if s == "ball":
count += 1
print(count)
attempts = 3
while attempts > 0:
print(attempts)
attempts -= 1
print("out of attempts")
species_tags = {
"ball": ["docile", "captive-bred"],
"burmese": ["large", "escape-risk"],
}
for species, tags in species_tags.items():
for tag in tags:
print(species, tag)
Control flow statements
A for loop and a while loop can both be redirected mid-run — cut short, skipped ahead by one pass, or wrapped up with a bit of code that only runs if nothing interrupted them. These keywords work identically in either loop type.
species = ["burmese", "rock", "ball", "blood"]
for s in species:
if s == "ball":
break
print(s)
Break
Exits the loop immediately, skipping everything left in it. Nothing after it runs, and anything left in the sequence (or any remaining passes of the condition) is skipped entirely.
for s in species:
if s == "ball":
break
print(s) # burmese rock
count = 0
while count < 5:
if count == 3:
break
print(count)
count += 1 # 0 1 2
Continue
Skips just the current pass, then keeps looping. The rest of the loop body doesn't run for that item, but the loop itself keeps going from the next item or the next check of the condition.
for s in species:
if s == "ball":
continue
print(s) # burmese rock blood
count = 0
while count < 5:
count += 1
if count == 3:
continue
print(count) # 1 2 4 5
Else
Runs once the loop finishes on its own — skipped entirely if break cut it short. Both for and while can end with an else block.
for s in species:
print(s)
else:
print("done") # burmese rock ball blood done
count = 0
while count < 3:
print(count)
count += 1
else:
print("done") # 0 1 2 done
Going further
pass placeholder
Temporarily fill an empty loop body when you're not ready to write the inside code yet. Python doesn't allow an empty block after a colon. pass does nothing, but acts as a placeholder until you're ready to add code so that the empty block won't cause a syntax error in the meantime. Covered in more detail on the Conditionals page.
for s in species:
pass # placeholder — does nothing, but prevents a syntax error
Run a loop control example
All the examples above, combined into one script:
species = ["burmese", "rock", "ball", "blood"]
for s in species:
if s == "ball":
break
print(s)
species = ["burmese", "rock", "ball", "blood"]
for s in species:
if s == "ball":
break
print(s)
count = 0
while count < 5:
if count == 3:
break
print(count)
count += 1
species = ["burmese", "rock", "ball", "blood"]
for s in species:
if s == "ball":
continue
print(s)
count = 0
while count < 5:
count += 1
if count == 3:
continue
print(count)
species = ["burmese", "rock", "ball", "blood"]
for s in species:
print(s)
else:
print("done")
count = 0
while count < 3:
print(count)
count += 1
else:
print("done")
species = ["burmese", "rock", "ball", "blood"]
for s in species:
pass
print("loop finished without doing anything each pass")