Visualize Python
Each picture runs the code beside it, one step at a time. Type your own lines under it and watch again. All visualizers.
- Your own codePaste any function and watch it run: each line, what changed, the calls in progress and the answer.Roadmap: How Python runs your code, Decisions and loops: if, for and while, Your first functions: def and return
- SlicesWatch a slice count its start and stop from the front or the back, then take its places one step at a time, backwards too.Lesson: My shop’s total says 0.30000000000000004 · Roadmap: Numbers, strings, slicing and f-strings
- Sets and lookupsWatch numbers go into a set’s table of slots, then one get looked up in a slot or two. A list would compare it with every item.Lesson: My import got 100 times slower when the data grew 10 times · Roadmap: list, tuple, dict and set, and what each operation costs
- A line on Python’s stackWatch one line become bytecode, then run: each value goes on a stack, and each operator takes the top two and puts one back.Lesson: Python says my formula has too many nested parentheses · Roadmap: How Python runs your code
- Names and objectsWatch names point at lists and dicts: two names on one list, and a copy that is only new on the outside.Lesson: I changed my copy, and the original changed too · Roadmap: Names, objects and references, Mutable and immutable objects
- Scope boxesWatch Python look for a name box by box: local, enclosing, global, built-in. And see a finished call’s box stay for a closure.Lesson: Python says my counter has no value · Roadmap: Scope and the LEGB rule, Closures
- ComprehensionsWatch a for loop skip a reading after each one it removes, then a comprehension build a new list, one part at a time.Lesson: I removed the bad readings, but one is still there · Roadmap: Comprehensions
- ExceptionsWatch an error go up through the calls in progress, past a call with no try, until an except that matches catches it.Lesson: My server ignored the port I set · Roadmap: Exceptions
- Files and pathsFollow a path one part at a time from the folder you ran the program from. See why the same path finds a file from one folder and not from another.Lesson: My script can’t find its file when I run it from another folder · Roadmap: Files and pathlib
- GeneratorsWatch a pipeline of generators read a log one line at a time, with almost nothing in memory.Lesson: Count errors in a 10 GB log · Roadmap: Iterators and generators
Your own code
Section titled “Your own code”Paste any function and write the call to watch. Each step shows the line, what changed, the calls in progress and the answer. Lists show index pointers (nums[i] marks i), dicts show as tables, and classes like ListNode and TreeNode show as diagrams.
Paste a function, then watch it run
Ctrl + Enter visualizesUnder every code editor in the lessons, Visualize does the same with the first test.
Slices
Section titled “Slices”A slice works out where it starts and stops, counting a negative place from the back. Then it takes a place at a time, by its step. The stop is never taken. Type your own text and slice under the picture, like -3: or ::2. The lesson: My shop’s total says 0.30000000000000004.
- s0-8
- t1-7
- r2-6
- e3-5
- s4-4
- s5-3
- e6-2
- d7-1
Step 1 of 12. s holds 8 characters. Each has a place counted from the front, 0 to 7, and one counted from the back, -8 to -1, shown under it.
s = "stressed" print(s[ ::-1])
Sets and lookups
Section titled “Sets and lookups”Numbers go into a set one at a time, each to the slot its hash picks, then one is looked up. A list would compare it with every item instead. Type your own numbers under the picture. The lesson: My import got 100 times slower when the data grew 10 times.
The set’s table: 8 slots
- Slot 0: empty
- Slot 1: empty
- Slot 2: empty
- Slot 3: empty
- Slot 4: empty
- Slot 5: empty
- Slot 6: empty
- Slot 7: empty
The same numbers in a list
None yet.
Step 1 of 16. seen = set() makes an empty set: a table of 8 slots, all empty.
seen = set() for x in [ 3, 11, 19, 27]: seen.add( x) print( 27 in seen)
A line on Python’s stack
Section titled “A line on Python’s stack”Python compiles a line to bytecode before it runs it. Each value goes on a stack, and each operator takes the top two and puts one answer back. Type your own formula and values under the picture, like a - b // c. The lesson: Python says my formula has too many nested parentheses.
Names
| a | 7 |
|---|---|
| b | 2 |
| c | 3 |
The stack is empty.
Step 1 of 7. Before any of line 2 runs, Python compiles it into 6 instructions for a stack. a, b and c already hold 7, 2 and 3.
a, b, c = 7, 2, 3 x = a - b // c # Python 3.14 runs line 2 as: LOAD_NAME a LOAD_NAME b LOAD_NAME c BINARY_OP // BINARY_OP - STORE_NAME x
Names and objects
Section titled “Names and objects”A name points to an object. It doesn’t hold one. Watch two names share one list, and a copy that is new only on the outside. Then type your own lines under the picture. The lesson: Names, objects and references.
Names
none yet
Objects
no lists, dicts or tuples yet
Step 1 of 8. Before the first line runs, there are no names and no objects.
a = [1, 2] b = a b.append( 3) c = a.copy( ) c.append( 4) del a print( b, c)
Scope boxes
Section titled “Scope boxes”Each call of a function gets a box for its names, drawn inside the box of the function it was made in. Watch Python look for a name box by box, and see a finished call’s box stay while a function made in it still uses it. The lesson: Python says my counter has no value.
Built-inPython’s own names
- len
- max
Globalrunning now
no names yet
Step 1 of 12. Before the first line runs, the global box is empty. The built-in box holds names like print and len.
count = 0 def make_counter(): count = 10 def tick( ): nonlocal count count += 1 return count return tick orders = make_counter( ) print( orders( ), count)
Comprehensions
Section titled “Comprehensions”A for loop keeps its place in a list by position. So removing a reading moves the next one back into a place the loop has done. A comprehension builds a new list instead, and runs its parts in an order you might not expect. The lesson: I removed the bad readings, but one is still there.
- 30
- -41
- -22
- 83
Step 1 of 10. readings holds 4 readings. 2 are below 0: errors. The loop should remove them.
readings = [3, -4, -2, 8] for r in readings: if r < 0: readings.remove( r) print( readings)
- 30
- -41
- -22
- 83
kept = []
Step 1 of 13. The comprehension starts a new, empty list. It reads readings, but never changes it.
readings = [3, -4, -2, 8] kept = [ r for r in readings if r >= 0 ] print( kept, readings)
Exceptions
Section titled “Exceptions”When a line raises an error, Python goes back through the calls in progress, newest first, until a try with a matching except catches it. A call with no try just stops. Type a port that isn’t a number, or one out of range, and watch where the error is caught. The lesson: My server ignored the port I set.
Top levelrunning now
Step 1 of 11. ConfigError is a new kind of error, made from ValueError, so a ConfigError is a ValueError too. Lines 4 to 14 only define functions: nothing in them runs yet.
class ConfigError(ValueError): pass def read_port( text): try: port = int( text) except ValueError as err: raise ConfigError( f"not a number: {text!r}") from err if not 1 <= port <= 65535: raise ConfigError( f"out of range: {port}") return port def load_config( settings): return { "port": read_port( settings[ "port"])} try: print( load_config( { "port": "8O80"})) except ConfigError as err: print( "Bad config:", err)
Files and paths
Section titled “Files and paths”A path that doesn’t start with / starts in the folder you ran the program from, not where the script is. Pick a folder to run from and a path, and follow it one part at a time: .. goes up a folder, and each name must be there. The lesson: My script can’t find its file when I run it from another folder.
The path, part by part
..dataprices.csv
- /
- home/
- sam/
- shop/
- data/
- prices.csv
- tools/you ran it from here
- report.pythe script
Step 1 of 7. You ran python /shop/tools/report.py from /shop/tools. That’s the program’s current folder, the one Path.cwd() gives.
# In a terminal, in /shop/tools: # python /shop/tools/report.py # In report.py: with open("../data/prices.csv", encoding="utf-8") as f: first = f.readline( )
Generators
Section titled “Generators”A generator hands over one item when asked, then waits. Chained together, they read a file one line at a time, so even a 10 GB log needs almost no memory. The lesson: Count errors in a 10 GB log.
app.log
- 09:00:01 INFO user 17 logged in (not read yet)
- 09:00:02 ERROR payment timed out (not read yet)
- 09:00:04 WARN slow query (not read yet)
- (blank line) (not read yet)
- 09:00:07 ERROR payment timed out (not read yet)
read_lineswaitingparsewaitingonly_errorswaitingsumwaiting
In memory now
Nothing yet
count = 0
Step 1 of 19. sum asks only_errors for an error. only_errors asks parse, and parse asks read_lines. Nothing has been read yet.
from collections.abc import Iterable, Iterator Entry = tuple[str, str, str] # ( time, level, message) def read_lines( path: str) -> Iterator[ str]: with open( path, encoding="utf-8", errors="replace") as f: yield from f def parse( lines: Iterable[ str]) -> Iterator[ Entry]: for line in lines: parts = line.split( maxsplit=2) if len( parts) < 2: continue # a blank or cut-off line message = parts[ 2].rstrip( ) if len( parts) == 3 else "" yield parts[ 0], parts[ 1], message def only_errors( entries: Iterable[ Entry]) -> Iterator[ Entry]: for entry in entries: if entry[ 1] == "ERROR": yield entry def count_errors( path: str) -> int: return sum( 1 for _ in only_errors( parse( read_lines( path))))