Name
A label in a namespace, such as x. It does not store a value. It points at an object. See Session 01.
Short definitions for the words this course uses again and again. Search, then jump back into the session that shows the idea in memory.
No terms match that search.
A label in a namespace, such as x. It does not store a value. It points at an object. See Session 01.
Everything in Python is an object: numbers, strings, lists, functions, classes, instances. An object has identity, a type, and a value. Objects live on the heap.
The connection from a name (or a container slot) to an object. Assignment creates or changes a reference. It does not copy the object unless you ask for a copy.
Which object something is, not what it looks like. id(x) and x is y test identity. Two names with the same identity are aliases.
== asks whether two objects have the same value. They can be equal and still be different objects.
Two or more names (or slots) pointing at the same object. Mutations through any alias are visible to all of them.
x = something_else makes the name point at a different object. The old object is unchanged; it just loses one reference.
Changing an object in place without creating a new one. lst.append(1) mutates. The object's id() stays the same.
A mutable object can change its contents (list, dict, set, instances). An immutable object cannot (int, str, tuple). "Changing" an immutable object always rebinds the name instead.
How many names or containers currently point at an object. In CPython, when this hits zero the object is freed immediately. Cycles need a separate collector.
The region of memory where objects live. Every Python object (an int, a list, a function, a class) is allocated on the heap. Names live in namespaces and point into it.
The pile of frames for calls currently in progress. Each call pushes a frame; each return pops one. A traceback is a printout of this stack. See Session 02.
Freeing objects nothing refers to any more. CPython does this mainly by reference counting: the instant a count hits zero the object is freed. A separate cyclic collector handles objects that only refer to each other.
CPython builds the integers -5 to 256 once at start-up and hands out the same object every time, so a = 42; b = 42 gives two names for one object. Those objects are never freed. It's an implementation detail, not a language guarantee, so never write code that depends on it.
CPython reuses some immutable objects, especially small integers (typically -5 to 256) and some strings. That is an optimisation, not a language rule. Do not write code that depends on it.
def creates a function object and binds a name to it. The body does not run at definition time. See Session 02.
The temporary workspace for one function call: local names, the current line, and the path back to the caller. Frames stack; the top one is running.
Name lookup order: Local, Enclosing function scopes, Global, Built-ins. Assignment creates a local name unless you declare global or nonlocal.
A parameter is a local name bound to the object the caller passed. Python does not copy that object. Mutating it is visible to the caller; rebinding the parameter is not.
An inner function plus the outer names it still needs after the outer call returns. nonlocal updates a remembered outer name instead of creating a new local.
Default values are created once, when the function is defined, and stored on the function object. A mutable default is shared across calls, which is a classic surprise.
An object whose contents are references to other objects. Lists, dicts, tuples, and sets are containers. See Session 03.
A new outer container filled with the same inner references. list.copy() and dict.copy() are shallow. Nested objects stay shared.
A recursive copy of nested objects. Use it only when you need nested independence. Some objects should stay shared on purpose.
An object with a stable hash, required for dict keys and set elements. Built-in immutables are usually hashable. Lists and dicts are not.
class creates a class object. It holds methods and class attributes. Calling the class creates an instance. See Session 04.
An object created by calling a class. It has its own identity and usually its own __dict__ for instance attributes.
The conventional name for the instance a method is operating on. Python passes it automatically when you write obj.method().
The mapping that stores an object's writable attributes. Instance attributes live on the instance dict. Methods usually live on the class dict.
Python looks on the instance first, then the class, then base classes (the MRO). That is why a class attribute is visible on every instance until one instance shadows it.
Accessing a function on an instance creates a method object that already remembers self. g.hello is not the raw function; it is hello bound to g.
The initialiser. After the instance exists, Python calls __init__(self, ...) to write attributes. It returns None. The caller receives the instance, not that return value. See Session 04.
The allocator. Calling a class first creates the instance, then initialises it. You almost never override __new__. The important picture: the object exists before __init__ runs.
A name stored on the class object. Every instance can read it until it assigns the same name on itself. A mutable class attribute (a list on the class) is one shared object.
A name stored in the instance __dict__. Assignment on an instance usually creates one of these. It shadows a class attribute of the same name for that instance only.
Method resolution order: the list of classes Python searches for an attribute. For class Dog(Animal) it is Dog → Animal → object. Subclasses do not copy methods; they walk this list.
The instance's link to its class. type(p) is Point and p.__class__ is Point are the same idea. Lookup follows this pointer after the instance dict misses.
An object you can loop over. It can produce an iterator via iter(obj). Lists, strings, dicts, and generators are iterable. See Session 05.
An object with state that yields one item at a time through next(). When it is exhausted it raises StopIteration. A for loop hides this protocol.
A function that uses yield. Calling it does not run the body; it returns a generator object. Each next() resumes a suspended frame until the next yield.
Work is done only when the next value is requested. A generator expression keeps one pending computation; a list comprehension builds every value immediately.
The signal that an iterator is exhausted. A for loop catches it and exits. The collection is unchanged; the iterator is spent. A new for on a list creates a new iterator. A generator does not rewind.
A callable that takes a function and returns a replacement. @deco above a def is shorthand for f = deco(f): your function is built, handed to deco, and the name is bound to whatever comes back. See Session 06.
The function a decorator returns. It usually calls the original and adds something around it. After decorating, your name points at the wrapper, not at the function you wrote.
The small box that keeps an outer name alive for an inner function. It is why a wrapper can still reach your original function after the decorator's frame is gone, and why that original is not garbage collected even though no name points at it. See Session 02.
A function you call to get a decorator, which is how a decorator takes arguments. In @repeat(2), repeat(2) runs first and returns the decorator that is then applied to your function.
functools.wrapsA decorator applied to a wrapper. It copies __name__, __qualname__, __doc__, __module__ and __annotations__ from the original function onto the wrapper, so tracebacks and help() keep telling the truth.
__wrapped__The attribute functools.wraps sets on a wrapper, pointing at the function underneath. It is how tools reach past a decorator to the real thing.
The single lock a thread must hold to execute Python bytecode in CPython. It exists because interpreter bookkeeping, reference counts above all, would be corrupted by unsynchronised threads. It's a property of CPython, not of the Python language. See Session 07.
A real operating-system thread, sharing the whole heap with its siblings. threading.Thread(...) only creates an object; the OS thread appears at .start(), which is when ident stops being None.
How long a thread may hold the GIL before the interpreter asks it to hand over: sys.getswitchinterval(), 5 ms by default. The request is only honoured at a safe point, chiefly a jump back to the top of a loop or a call into a function.
A bug whose outcome depends on the order in which threads happen to run. The classic shape is read-modify-write: two threads read the same value, both compute from it, and the second write erases the first. The GIL doesn't prevent this. It only keeps single bytecodes indivisible.
An operation that cannot be interrupted partway. In CPython one bytecode is atomic; one line of your source usually is not, because it compiles to several. If a switch can land in the middle, the operation is not atomic.
threading.Lock, an object only one thread may hold at a time. Wrapping a read-modify-write in with lock: makes it indivisible with respect to every other thread that uses the same lock. A thread waiting for a lock is in a different queue from a thread waiting for the GIL.
Two threads each holding a lock the other needs. Neither can ever proceed and no error is raised. The program just stops. Prevented by always acquiring locks in the same order, or by holding only one at a time.
The question that picks your tool. I/O-bound code is slow because it waits, and threads and asyncio overlap the waiting. CPU-bound code is slow because it computes, and only a second process (or a library that releases the GIL) adds a second core.
A second operating-system process running a second interpreter, with its own heap and its own GIL. Two processes really do execute Python at the same instant. They share no objects at all, so anything passed between them is copied.
Turning an object into bytes so it can cross a process boundary and be rebuilt on the other side. The result is an equal object at a different address, never the same object. Lambdas, open files, connections and locks cannot be pickled.
How multiprocessing creates a child: spawn, fork or forkserver. Check yours with mp.get_start_method(). Where the child re-imports your module (spawn is the default on Windows and macOS), the if __name__ == "__main__": guard is required or the child spawns its own child.
The object asyncio uses to drive coroutines on a single thread. It keeps a list of tasks that are ready and a list of things being waited on, runs one task until it suspends, then picks the next.
What calling an async def function gives you: an object that has run none of its body, exactly like a generator object. It suspends at await, keeping its frame and locals, and the event loop resumes it later. Never awaiting one produces RuntimeWarning: coroutine ... was never awaited.
await (yield point)The only place a coroutine can be suspended, and a word you typed, visible in the source. This is what makes asyncio cooperative: between two awaits nothing else runs, so no lock is needed. Threads, by contrast, are pre-empted wherever the interpreter allows.
An object that drives one coroutine on the event loop. A coroutine on its own does nothing; asyncio.gather or create_task wraps it in a Task and hands it to the loop.
An optional CPython build with no GIL, added by PEP 703. It was experimental in 3.13, is officially supported in 3.14, and is separate from the default build, which still has one. Check with sys._is_gil_enabled(), which exists from 3.13 onwards. It makes CPU-bound threads scale. It doesn't fix any race condition, and it makes the ones you have easier to hit.
An ordinary instance of a class that inherits from BaseException. raise X evaluates X (usually a constructor call) and then starts the object climbing the call stack. It carries args, a __traceback__, and links to related exceptions. See Session 08.
What happens between raise and the handler: each frame without a matching except around the current line is popped, locals and all, just as on return, and the exception arrives in the caller at the line that made the call. If it runs out of frames, the traceback is printed and the process exits with status 1.
The record of frames an exception has passed through, outermost first, reachable as exc.__traceback__ and walked with tb_next. The printed Traceback (most recent call last) is that chain written out one frame per entry; read it from the bottom, where the error and the raise site are.
try / except)An except clause covering the line an exception arrived on. Clauses are tested in order with isinstance, so a clause catches the class it names and every subclass. The frame that catches keeps its desk; the name bound by as err is deleted automatically when the block ends, to break the cycle between the exception, its traceback and the frame.
finallyA block that runs on every way out of a try: a clean finish, a handled exception, an unhandled one, or a return. The pending result is parked while it runs and resumed afterwards. A return inside finally replaces that result and throws away an exception in flight. Python 3.14 warns about it.
Any object whose class defines __enter__ and __exit__. with EXPR as name: calls __enter__ and binds its return value to name, runs the body inside an invisible try, and then calls __exit__(exc_type, exc, tb), with three Nones on success. If __exit__ returns a true value the exception is suppressed. @contextlib.contextmanager builds one from a generator: the code before yield is the enter half, after it the exit half.
An exception raised while another is being handled gets __context__ set to the one being handled; the traceback then says During handling of the above exception, another exception occurred. raise New from old also sets __cause__, and the sentence becomes The above exception was the direct cause of the following exception. from None hides the earlier one.
Exceptions are classes with parents: KeyError and IndexError are LookupErrors, most things you raise yourself are Exceptions, and KeyboardInterrupt and SystemExit sit directly under BaseException so that except Exception lets a program be interrupted. A bare except: catches those too, which is why it is almost always a mistake.
An ordinary object, an instance of the class module, made the first time a file is imported. Its __dict__ holds the file's top-level names, so config.TIMEOUT is a lookup in that dict, and a function defined in the file uses it as its globals. See Session 09.
importFour moves. Look the name up in sys.modules. On a miss, find the file on sys.path, create a module object, register it, and run the file with the module's __dict__ as its globals. Then bind a name in the namespace that ran the import. A module's body runs once per process, however many files import it.
sys.modulesThe import cache: a dict from module names to module objects. A module goes in before its body runs, which is how a circular import can find it half built, and comes out again if the body fails. A second import of the same name is one lookup here and runs nothing.
from ... importfrom config import retries imports config, then binds retries in your namespace to whatever config.retries points at right then, much like retries = config.retries. It doesn't bind config, and it doesn't follow later rebinding inside the module. Mutating a shared object is still visible through both names.
__name__ and the main guard__name__ is set before a module's body runs: "__main__" for the script you started (or ran with python -m), and the module's own name when it is imported. if __name__ == "__main__": runs a block only when the file is the program.
Two modules that import each other. The second import finds the first module in sys.modules before its body has finished, so reading a name it hasn't defined yet raises AttributeError, or ImportError: cannot import name for a from-import. Read the names inside functions instead, or move them into a third module.
sys.pathThe list of folders import searches, in order, after the modules built into the interpreter. Entry 0 is the folder of the script you ran, so a file there with a library's name (random.py, requests.py) hides the real one from the whole program. module.__file__ tells you which file you got.
A module that can hold submodules, usually a folder with an __init__.py. import shop.orders runs shop/__init__.py first, then shop/orders.py, caches both, and binds the name shop. The package's __path__ lists the folders its submodules are searched in.
__pycache__The folder where Python saves compiled bytecode for imported modules, as files like config.cpython-313.pyc, so the next start can skip compiling when the source hasn't changed. It speeds up start-up, not running time, and it is safe to delete.
importlib.reloadRuns a module's file again into the same module object, so code that reads config.name sees the new values. Names copied out earlier with from-imports, and instances of the old classes, still point at the old objects, which is why restarting is often simpler.