Lesson 4 / 25

A Loop with Every Guard

Combine step, token and time limits in one reusable budget object.

One object owns the limits

Scattering if checks through the loop makes limits easy to forget. A small Budget object tracks steps, tokens and elapsed time, and answers one question: "may we continue, and if not, why?" The loop asks it once per turn.

A Budget class

This ran as shown. Charging 3,000, 4,000 and 4,000 tokens trips the 10,000-token limit on the third step, and reason() says why.

class Budget:
    def __init__(self, max_steps, max_tokens):
        self.max_steps, self.max_tokens = max_steps, max_tokens
        self.steps = self.tokens = 0

    def charge(self, tokens):
        self.steps += 1
        self.tokens += tokens

    def reason(self):
        if self.steps >= self.max_steps: return "step limit"
        if self.tokens >= self.max_tokens: return "token budget"
        return None

b = Budget(5, 10_000)
for used in (3000, 4000, 4000):
    b.charge(used)
    print(b.steps, b.tokens, b.reason())

Output:

1 3000 None
2 7000 None
3 11000 token budget

Return a reason, not just a boolean

"Stopped" is useless in a log. "Stopped: token budget at step 3" tells you what to fix. Make every stop carry a reason string.

Quick check: Why centralise limits in one Budget object?

  • Scattered checks are easy to forget or apply inconsistently
  • Python requires classes
  • It makes the model smarter
  • It removes the need for tests
Answer

Scattered checks are easy to forget or apply inconsistently — One owner of the limits keeps checks consistent and the stop reason easy to report.