Control Flow
Control flow is how code makes decisions and repeats work. Two ideas — if and for — cover most of what an agent needs, and combining them is already most of what a triage rule does.
if, elif, else
severity = "P1"
if severity == "P1":
action = "page the on-call engineer"
elif severity == "P2":
action = "raise a ticket"
else:
action = "log and review tomorrow"
print(action)- the colon opens the block, the indentation contains it
elifis Python'selse if, spelled as one wordelseis optional, and there can be as manyelifbranches as you like
Only the first matching branch runs. Once one is taken, the rest are skipped.
If you come from a brace language, the missing { } is the thing to get used to. Indentation is not formatting here; it is the syntax. There is a page on it.
The comparisons
a == b
a != b
a > b
a >= b
a is None
a is not None
"x" in items
"x" not in itemsTwo that matter more than they look:
==compares values. In Java you would write.equals(). Python's==is the one you want almost always.iscompares identity — whether two names point to the same object. Use it only withNone.
if owner is None:
...owner == None works but is not idiomatic, and is is the form every reviewer expects.
Combining conditions
if severity == "P1" and owner is None:
assign_and_page()
if severity == "P1" or customer_tier == "diamond":
escalate()
if not resolved:
follow_up()and, or, not — words, not &&, ||, !. That is the whole difference.
Truthiness
Python treats several values as false in a condition:
if not items:
print("nothing to process")Empty is false: 0, "", [], {}, None, False. Everything else is true.
if items: reads well and is idiomatic for "is there anything here". But it is not the same question as "was this set", because an empty list is also false.
if owner:
...
if owner is not None:
...The first skips an empty string. The second does not. When you specifically mean "was a value provided", say is not None.
for loops
Python's for walks over a collection. There is no for (int i = 0; i < n; i++).
tickets = ["P1", "P3", "P2", "P1"]
for t in tickets:
print(t)If you need the position too, use enumerate:
for i, t in enumerate(tickets):
print(i, t)If you need a count of repetitions, use range:
for i in range(3):
print("attempt", i)range(3) gives 0, 1, 2 — it stops before the number you give it. range(1, 4) gives 1, 2, 3.
Two collections at once
names = ["Amit", "Sara"]
roles = ["Architect", "Analyst"]
for name, role in zip(names, roles):
print(name, "-", role)Dictionaries
for key, value in member.items():
print(key, "=", value)Looping a dictionary directly gives keys. .items() gives pairs.
while loops
Repeat while something remains true. Used when you do not know the number of turns in advance — which is exactly the shape of an agent loop.
attempts = 0
while attempts < 3:
if call_api():
break
attempts += 1Two dangers, and both bite in agent code.
A loop that never ends. If nothing inside changes the condition, it runs forever. In an agent that means real money at the model provider.
No ceiling. Any agent loop needs a maximum number of turns, not only a success condition:
MAX_ITERATIONS = 8
steps = 0
while not done and steps < MAX_ITERATIONS:
step()
steps += 1The second condition is not optional. A model that keeps deciding to call one more tool will happily do so until you stop it.
break, continue, else
for t in tickets:
if t == "P1":
print("found one")
break
for t in tickets:
if t == "P4":
continue
process(t)breakleaves the loop immediatelycontinueskips to the next item
Python also allows an else on a loop, which runs only if the loop finished without breaking:
for t in tickets:
if t == "P1":
break
else:
print("no P1 found")Rare, occasionally elegant, and confusing enough that many teams avoid it. Worth being able to read.
Putting it together
Most triage logic is a loop, a condition and a dictionary.
tickets = [
{"id": 1, "severity": "P1", "owner": None},
{"id": 2, "severity": "P3", "owner": "network"},
{"id": 3, "severity": "P1", "owner": "database"},
]
for t in tickets:
if t["severity"] == "P1" and t["owner"] is None:
print(f"ticket {t['id']}: assign and page")
elif t["severity"] == "P1":
print(f"ticket {t['id']}: page {t['owner']}")
else:
print(f"ticket {t['id']}: queue")Nothing here is advanced, and it is genuinely most of what a rules engine does. The interesting part of an agent is that a model decides the branch instead of you — but the surrounding shape is this.
A shorter form
When a simple if/else only chooses between two values:
label = "urgent" if severity == "P1" else "normal"This is Python's ternary, and it reads value-first: this, if condition, else that. Use it for one-line choices, not for logic.
For building a list from a loop, there is a shorter form again — a comprehension — which has its own page.
Coming from Java or C#
| Concept | Java / C# | Python | ||
|---|---|---|---|---|
| Else-if | else if | elif | ||
| And / or / not | &&, `\ | \ | , !` | and, or, not |
| Value equality | .equals() | == | ||
| Reference equality | == | is | ||
| Enhanced for | for (T x : xs) | for x in xs | ||
| Counted loop | for (int i...) | for i in range(n) | ||
| Switch | switch | no switch — use elif, or a dictionary | ||
| Ternary | c ? a : b | a if c else b | ||
| Blocks | { } | indentation |
Two habits to change. First, == and is are effectively swapped compared with Java, so reach for == by default. Second, there is no switch: for many branches, a dictionary of functions is the idiomatic replacement, and that pattern is worth knowing because it is how tool dispatch works.
Common mistakes
else ifinstead ofelif.&&and||instead ofandandor.- A missing colon at the end of the line.
if x:when you meantif x is not None:, silently skipping empty values.- A
whileloop with no maximum, which in agent code costs money. - Modifying a list while looping it, which skips items.
Practise this
- Write an
if/elif/elseover a severity value and check every branch. - Loop over a list of dictionaries and print one field from each.
- Write a
whileloop with both a success condition and a maximum number of attempts. - Prove to yourself that
if []:is false but[] is not Noneis true.