Not the exercise, just the tool. append puts something on top, pop takes the top one off, and [-1] looks at it without taking it. The last thing in is always the first thing out.
stack = []
stack.append("plate 1")
stack.append("plate 2")
stack.append("plate 3")
print(stack[-1]) # -> plate 3 the top, still there
print(stack.pop()) # -> plate 3 the top, now removed
print(stack.pop()) # -> plate 2
print(stack) # -> ['plate 1']
print(bool([])) # -> False an empty stack is falsy
- Last in, first out
Brackets close in reverse order of opening, which is exactly the order a stack gives things back. That match is the whole idea.
- pop on an empty list raises
[].pop() is an IndexError. A closing bracket with nothing open means popping an empty stack, so that case has to be checked first, not caught afterwards.
- Empty at the end means done
Whatever is still on the stack when the loop finishes was opened and never closed. Checking that is the step people forget.
When you meet a closing bracket, which opening bracket must it match? Not any of them: the one opened most recently that is still open.
Keep a list of the brackets that are open. Opening: append. Closing: the top of the list must be its partner, and then you pop it.
A dict from each closer to its opener, pairs = {')': '(', ']': '[', '}': '{'}, saves three ifs. For each character: an opener goes on the stack; a closer needs a non-empty stack whose top is pairs[char]. At the end, the stack must be empty.
def is_balanced(text): pairs = {')': '(', ']': '[', '}': '{'} stack = [] for char in text: if char in '([{': stack.____(char) elif char in pairs: if not stack or stack.pop() != pairs[____]: return False return not ____
def is_balanced(text):
pairs = {")": "(", "]": "[", "}": "{"}
stack = []
for char in text:
if char in "([{":
stack.append(char)
elif char in pairs:
if not stack or stack.pop() != pairs[char]:
return False
return not stack
`or` stops at the first true operand, so an empty stack returns False without ever reaching pop, and a stray closer is an answer rather than an IndexError.
Adding < > later is one entry, not another branch in three places. It also makes "is this a closer?" a dict lookup.
Why counting fails ("([)]"), why a stack fits (brackets close in reverse order of opening), and that each character is looked at once. Mention the empty-stack check before you are asked.
This checks bracket characters and nothing else, so a bracket inside a string, "print(')')", counts as a real one. Checking actual code means skipping strings and comments first, which is a tokeniser’s job; Python’s own ast module will tell you whether Python code parses at all.
- Return the position of the first bracket that breaks the balance instead of False, so an editor could underline it.
- Ignore brackets inside single- or double-quoted strings.
- Add < and > as a fourth kind of bracket, and notice how little changes.