Python Basics

Control Flow

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
  • elif is Python's else if, spelled as one word
  • else is optional, and there can be as many elif branches 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 items

Two that matter more than they look:

  • == compares values. In Java you would write .equals(). Python's == is the one you want almost always.
  • is compares identity — whether two names point to the same object. Use it only with None.
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 += 1

Two 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 += 1

The 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)
  • break leaves the loop immediately
  • continue skips 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#

ConceptJava / C#Python
Else-ifelse ifelif
And / or / not&&, `\\, !`and, or, not
Value equality.equals()==
Reference equality==is
Enhanced forfor (T x : xs)for x in xs
Counted loopfor (int i...)for i in range(n)
Switchswitchno switch — use elif, or a dictionary
Ternaryc ? a : ba 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 if instead of elif.
  • && and || instead of and and or.
  • A missing colon at the end of the line.
  • if x: when you meant if x is not None:, silently skipping empty values.
  • A while loop with no maximum, which in agent code costs money.
  • Modifying a list while looping it, which skips items.

Practise this

  • Write an if/elif/else over a severity value and check every branch.
  • Loop over a list of dictionaries and print one field from each.
  • Write a while loop with both a success condition and a maximum number of attempts.
  • Prove to yourself that if []: is false but [] is not None is true.

Try It Yourself

Copy this into a new Colab notebook and run it. Nothing to install.

example.py
1# If/elif/else - handling different response types
2response_type = "function_call"
3
4if response_type == "text":
5 print("Processing text response...")
6elif response_type == "function_call":
7 print("Executing function call...")
8elif response_type == "error":
9 print("Handling error...")
10else:
11 print("Unknown response type")
12
13# For loop - processing messages
14messages = [
15 {"role": "user", "content": "Hello"},
16 {"role": "assistant", "content": "Hi there!"},
17 {"role": "user", "content": "How are you?"}
18]
19
20for msg in messages:
21 print(f"{msg['role'].upper()}: {msg['content']}")
22
23# While loop - retry logic
24max_retries = 3
25attempt = 0
26success = False
27
28while attempt < max_retries and not success:
29 attempt += 1
30 print(f"Attempt {attempt}...")
31 # Simulate success on third try
32 if attempt == 3:
33 success = True
34 print("Success!")
35
36# List comprehension - filter user messages
37user_messages = [m["content"] for m in messages if m["role"] == "user"]
38print(f"\nUser messages: {user_messages}")