Keentune
Python, oriented
12 chapters
·
about 18 min read
·
free
Python's shape follows from one fact: a name is a label attached to an object, and nothing more. Assignment binds a label and never copies, so two names can watch the same list change underneath them. Objects supply their own behaviour through dunder methods, so len(x), x == y and for i in x are the object answering a protocol rather than the language doing something built in. And the guarantees you might expect — type checking, privacy, atomic updates — are conventions upheld by a tool or by you, not by the runtime. Nearly every Python surprise is one of those three facts arriving unannounced.
Each chapter opens with the short version. Tap one to read the detail.
Names, objects, and the copy that never happens
~2 min
Every value is an object with an identity, and a name is just a label bound to one. Assignment rebinds labels rather than copying, so mutating through one name is visible through every other name for that object. is asks whether two labels point at the same object; == asks the object itself.
Arithmetic, with the sharp edges named
~2 min
/ always produces a float and // floors toward negative infinity, so -7 // 2 is -4, not -3. Binary floats cannot hold most decimal fractions, so 0.1 + 0.2 is not 0.3, and round breaks ties to even. Integers, unusually, never overflow.
Lists, dicts and sets — and what they actually store
~2 min
Containers store references, not values, so [[0] * 3] * 3 is one row referenced three times. Dicts and sets are hash tables: keys must be hashable, and keys that compare equal are the same key, so 1, 1.0 and True all land in one entry.
Text is not bytes
~2 min
A str is a sequence of Unicode code points; bytes is a sequence of integers, and indexing one gives you a number rather than a one-byte slice. Converting between them can fail, so name the encoding at every boundary. And strip takes a set of characters, not a prefix.
Looping: comprehensions, laziness, one-shot iterators
~2 min
for asks for an iterator and pulls until it stops, and an iterator is consumed exactly once — a generator you have already read is not empty-looking, it is empty. Brackets build a list now; parentheses build something lazy. and and or return an operand, not a bool.
Functions, defaults and decorators
~2 min
A def is a statement that executes: it evaluates the default values once, at definition time, and binds the result to a name. A mutable default is therefore shared by every call that omits it. A decorator is just name = decorator(name), which is why it can replace the function outright.
Classes, and when a dataclass is enough
~2 min
Anything assigned in the class body belongs to the class and is shared by every instance; only self.x = ... is per-instance. super() follows the resolution order of the actual object, not your class's own parent. For a class that is mostly fields, @dataclass writes the boilerplate.
Failing, and cleaning up
~2 min
An except clause matches the class named and everything derived from it, so except Exception is the honest wildcard — while a bare except: also swallows keyboard interrupts and interpreter exit. finally always runs, and a return inside it discards the exception entirely.
Modules, environments, and which Python you are running
~2 min
An import searches sys.path, executes the module once, and caches it in sys.modules; every later import returns that cached object. The directory you run from is on that path, so a file named random.py shadows the standard library. A virtual environment is that same path mechanism, aimed at your project.
Threads, processes and async
~2 min
On CPython one thread runs bytecode at a time, so threads overlap waiting rather than adding compute; processes add compute and pay in copying. async adds neither by itself — it lets one thread interleave waits, and a single blocking call stalls every task on the loop.
Files, encodings, and the batteries around them
~2 min
open makes two decisions for you unless you make them: the mode, where "w" truncates the file the moment it opens, and the text encoding, which defaults to whatever the machine prefers. That pattern repeats across the standard library — the defaults are conservative, not clever.
Checking what the runtime will not
~3 min
The runtime checks almost nothing you write down: annotations are recorded and ignored, and assert statements disappear entirely under python -O. Both are claims addressed to tools you run separately — a type checker and a test suite — which is why "it ran" and "it is correct" are different statements.
See the full Python curriculum
Written by Keentune. We are not affiliated with or endorsed by the organizations whose documentation informs this guide, and any linked sources belong to their respective owners.
All exam, test, and product names and trademarks are the property of their respective owners and are used here for identification and reference only. Keentune is independent study practice — not affiliated with, authorized, or endorsed by any of these organizations.
© 2026 SportaApp LLC