Python is dynamically typed because types are checked at runtime, and strongly typed because values do not silently coerce across unrelated types.
- Names bind to objects
- Type checks happen at runtime
- Explicit conversion is preferred
Encapsulation organizes state and behavior, abstraction hides unnecessary detail, polymorphism lets different objects satisfy the same interface, and composition often keeps designs looser than inheritance.
- Composition favors explicit relationships
- Polymorphism is about substitutable behavior
- Python uses conventions more than strict access control
Python strings are Unicode text, while bytes are encoded binary data that must be encoded or decoded at boundaries.
- Escapes like `\n` and `\t` are special characters
- Unicode represents text code points
- Encoding converts text to bytes and decoding converts bytes to text
Catch narrow exceptions, add context, and avoid swallowing failures silently.
- Prefer specific except
- Use finally for cleanup
- Log enough context
Python file work combines open modes like read, write, and append with text or binary handling, and context managers ensure resources close correctly.
- Use `with` for cleanup
- `pathlib` makes path logic clearer
- `contextlib` helps build reusable context managers
Functions are first-class values in Python, so you can pass them around, return them, and build closures or higher-order helpers.
- Lambda is for small anonymous functions
- Closures capture enclosing state
- Higher-order functions accept or return callables
The GIL lets only one Python thread execute Python bytecode at a time in CPython.
- Affects CPU-bound threads
- I O waits still benefit
- Use multiprocessing for CPU-heavy work
Python primarily uses reference counting, with cyclic garbage collection to clean objects that reference each other.
- Reference count frees many objects immediately
- Cycles need GC pass
- Del methods can complicate cleanup
Inheritance reuses behavior, overriding customizes it, `super()` follows the method resolution order, and multiple inheritance works only if the MRO stays coherent.
- Override behavior intentionally
- `super()` cooperates with MRO
- Multiple inheritance needs careful design
Instance methods work with object state, class methods work with the class, static methods are utility functions inside the namespace, properties expose attribute-style access, and dataclasses reduce boilerplate.
- Choose method type based on state access
- Properties can validate or compute values
- Dataclasses generate common methods automatically
Python source is usually compiled to bytecode first, then executed by the interpreter's virtual machine.
- Source is not run directly
- CPython compiles to bytecode
- Execution still happens inside an interpreter
An iterable can produce an iterator, an iterator yields values one at a time, generators are a compact way to build iterators, and advanced generator methods can push values or exceptions back in.
- Iterables and iterators are not the same
- Generators implement the iterator protocol for you
- `send`, `throw`, and `close` control generator execution