Python Atlas
Core language

Core language

A systems-level guide to Python names, objects, protocols, types, control flow, and reusable design.

Python is compact at the surface, but its small set of rules composes into a rich object model. Advanced Python is less about memorizing syntax and more about predicting what the interpreter will do with names, objects, scopes, protocols, and exceptions.

A useful mental model

Keep these distinctions clear:

  • Names refer to objects. Assignment binds a name; it does not copy an object.
  • Objects carry identity, type, and value. Mutability belongs to the object, not the variable.
  • Scopes resolve names. Python searches local, enclosing, global, then built-in scopes (LEGB).
  • Protocols define behavior. Iteration, context management, arithmetic, and many other operations are expressed through special methods.
  • Modules are objects. Importing executes module code once per interpreter and stores the resulting module in sys.modules.
  • Exceptions are part of control flow. They separate the normal path from recovery while preserving failure context.
  • Type hints are metadata. Static tools consume them; Python generally does not enforce them at runtime.
from dataclasses import dataclass


@dataclass(frozen=True, slots=True)
class Point:
    x: float
    y: float


origin = Point(0.0, 0.0)
alias = origin

assert alias is origin       # identity
assert alias == origin       # value equality
assert isinstance(origin, Point)

alias = origin creates a second name for the same object. The generated Point.__eq__ gives value equality, while is still tests identity. Confusing these operations causes subtle bugs around mutable defaults, caching, and shared state.

Follow behavior, not categories

Python code is usually most reusable when it asks for the smallest behavior it needs. A function that loops over values should accept an Iterable, not require a list. A serializer can accept any object implementing a Protocol, without forcing it into an inheritance hierarchy.

from collections.abc import Iterable


def product(values: Iterable[int]) -> int:
    result = 1
    for value in values:
        result *= value
    return result


assert product([2, 3, 4]) == 24
assert product(value for value in range(2, 5)) == 24

The broad input contract admits lists, tuples, generators, and custom iterables. The concrete return type remains precise. This pattern—general inputs, specific outputs—usually improves both usability and static analysis.

The design spectrum

Choose the least powerful abstraction that clearly expresses the problem:

  1. A literal or expression for a fixed value.
  2. A function for a stateless transformation.
  3. A closure for a small amount of private captured state.
  4. A data class for structured data and value semantics.
  5. A class for cohesive state plus behavior.
  6. A protocol or abstract base class for polymorphism across implementations.
  7. A decorator or context manager for behavior around an operation.

More machinery is not automatically more maintainable. Every abstraction adds names, contracts, indirection, and failure modes. Extract repeated concepts, not merely repeated lines.

How to use this section

The pages build from namespace boundaries to higher-level composition:

  1. Modules and imports establish how code is loaded and APIs are exposed.
  2. Classes and the data model explain object-oriented behavior.
  3. Protocols and abstract base classes define reusable boundaries.
  4. Decorators and closures model wrapped behavior and captured state.
  5. Annotations express machine-checkable contracts.
  6. Exceptions and context managers make failure and cleanup explicit.
  7. Iterables, generators, and comprehensions model data flow.

Examples target modern Python and use built-in generic syntax such as list[str] and unions such as str | None. Where version-sensitive features matter, the text calls them out explicitly.

Tradeoffs to keep visible

  • Dynamic behavior speeds exploration but moves some failures to runtime.
  • Static typing catches interface mistakes but cannot prove all runtime behavior.
  • Concise expressions reduce noise until they hide order, state, or failure.
  • Magic methods integrate beautifully with Python but create surprising code when their semantics do not match the operator they implement.
  • Inheritance can share behavior but tightly couples base and derived classes.
  • Lazy generators save memory but defer work and errors.

Common pitfalls

  • Using is for value comparison.
  • Mutating an object through one alias and expecting another alias to be isolated.
  • Importing from internal modules and accidentally making them public API.
  • Treating type annotations as runtime validation.
  • Catching Exception too early and losing actionable failure information.
  • Reusing an exhausted iterator.
  • Hiding important side effects in decorators or special methods.

Practice

  1. Explain the difference among a name, an object, and a type without using the word “variable.”
  2. Write a function that accepts any iterable of strings and returns a tuple. Test it with a list and a generator.
  3. Find one class in a project that could be a pure function or frozen data class. State what complexity would disappear after the change.
  4. For an API you know, identify its public contract, concrete implementation, failure model, and cleanup responsibilities.

LEARNING RECORD

Finish this lesson

Mark it complete when you can explain the main decision without looking.

KNOWLEDGE CHECK

Core language

1After `alias = origin`, what relationship does Python establish?
2A function loops over its input exactly once. Which annotation states the narrowest useful contract?