S
Saurav Danej
90-Day AI/ML LinkedIn Content System
← All days
13
Day 13 of 90Python

Type hints + dataclasses — Python at scale

POST 1 of 5 MorningPythonConcept

Type hints — Python's safety net

There's a question that comes up in every Python team transitioning from 'small scripts' to 'serious project': do we add type hints? The right answer is yes — and I'll tell you why most teams hesitate before they finally do.

The hesitation usually goes 'type hints are extra work and they don't even run'. The first part is true. The second part misses the point.

Type hints don't execute at runtime. They're metadata attached to functions and variables. The interpreter ignores them when running your code. No overhead, no behaviour change.

But.

IDEs use them for accurate auto-complete and 'go to definition'. The difference between hint-less Python and hinted Python in PyCharm or VS Code is night and day.

mypy and pyright (static type checkers) catch entire classes of bugs before you run the code. Wrong return type, missing None handling, calling a function with the wrong argument types — caught at lint time, not runtime.

They serve as inline documentation. A signature 'def embed(text: str, model: str = small) -> list[float]' tells you everything you need to know to call the function. No need to read the body.

Dataclasses, Pydantic, FastAPI, and most modern libraries USE the hints to generate behaviour — auto-validating inputs, generating API schemas, building serialisers, generating docs.

The cost of type hints — about a 10% increase in code volume. The benefit — catching bugs you'd otherwise meet in production, plus the IDE wins, plus framework integration.

In 2026, untyped Python on a multi-developer project is a code smell. Strict mypy on a new project is a non-negotiable. The path from 'hints are optional' to 'hints are expected' is a one-way road, and the ecosystem already finished the journey. Catch up.
#Python#AI#100DaysOfCode#BuildInPublic#PythonProgramming#TypeHints
POST 2 of 5 MiddayPythonDeep dive

The type hints you'll actually use

Most type-hint tutorials lead with exotic generics and TypeVars. Skip that for now. Eight type-hint primitives cover 95% of the Python you'll write.

The primitives — str, int, float, bool. Self-explanatory. Use them everywhere a function takes or returns a basic value.

Collections — list[T], dict[K, V], set[T], tuple[T, ...]. The new (3.9+) syntax for generic collections. Old syntax was List[T], Dict[K, V] from typing. New syntax is shorter and lives directly on the built-in types. Use the new syntax in any new code; the old still works for compatibility.

Optional values — Optional[T] or T | None (3.10+ pipe syntax). Used for parameters that can be None and return values that might be missing. The pipe syntax is more readable. 'def find(name: str) -> User | None' is clear at a glance.

Fixed string options — Literal['a', 'b', 'c']. The parameter must be exactly one of these strings. Type-checker catches typos. Useful for mode flags, pillar names, status enums.

Function signatures — Callable[[int], str]. A function that takes one int and returns a string. Use when accepting callbacks or higher-order functions.

Escape hatch — Any. Disables type-checking for that value. Use when you genuinely don't care about the type, or when interfacing with un-typed third-party libraries. Sparingly.

Dicts with known keys — TypedDict. {'name': str, 'age': int}. Stricter than dict[str, Any]. Useful for API responses, JSON shapes.

Duck typing, statically — Protocol. Define an interface by methods, not by inheritance. Anything implementing the methods satisfies the protocol. The type-system way to do duck typing.

Learn these eight. Read 95% of typed Python with no friction. Reach for the more exotic generics (TypeVar, Generic[T], Concatenate) when the situation actually calls for it — usually when you're writing a library, not application code.
#Python#AI#100DaysOfCode#BuildInPublic#PythonProgramming#PythonTyping
POST 3 of 5 AfternoonPythonCode

@dataclass — kill the boilerplate

Look at the snippet. Five lines. That's a Document class with id, text, score, and tags fields. Auto-generated __init__, __repr__, __eq__, __hash__ (because frozen=True), and immutability. Try writing it without @dataclass and you're at 30+ lines, easy.

@dataclass auto-generates the methods you'd otherwise hand-code from the fields you declare with type hints. It's the single biggest reduction in Python boilerplate to land in the last decade.

Let me unpack the example.

@dataclass(frozen=True) on the class. The frozen=True flag makes instances immutable — assignments to fields after construction raise FrozenInstanceError. Combined with type hints on the fields, this gives you __init__, __repr__, __eq__, __hash__ for free.

Field declarations look like type hints — id: str, text: str, score: float = 0.0. The dataclass decorator inspects them at class-creation time and generates the __init__ to accept them in order, with defaults applying to fields that have them.

Use this for any 'plain data' class — domain objects, value types, request/response shapes, configuration. The 80% of classes that exist mainly to bundle data should be dataclasses, not handwritten classes.

The other 20% — classes with custom validation, deferred field initialisation, complex __init__ logic — reach for Pydantic. Same idea, more powerful, runtime validation, integrates with FastAPI. We use Pydantic in the agent and RAG weeks.

The gotcha worth knowing — mutable default fields need field(default_factory=list) instead of just '= []'. Otherwise you hit the same mutable-default trap from Day 3. Dataclasses warn about this, so it's hard to get wrong.

Write less. Get more.
#Python#AI#100DaysOfCode#BuildInPublic#PythonProgramming#PythonDataclass
POST 4 of 5 EveningPythonTip

Run mypy in CI from day one

If you're starting a new Python project today, the single highest-leverage decision you can make is — add mypy to CI on day one. Strict mode. No exceptions allowed.

Why day one? Because adding mypy to a mature project is a brutal slog. You face thousands of errors, half of them genuine bugs, half of them stylistic warnings. You triage for weeks, suppress hundreds with #type: ignore, and ship a half-typed codebase that gives you 30% of the value of a fully-typed one.

Add mypy on day one and the experience is the opposite. The codebase has zero errors at the start. Every PR adds new code and (occasionally) new type errors. You fix them as you go. The cost is bounded — usually 5-15 minutes per PR. The codebase stays cleanly typed from start to finish.

The minimum setup. Add 'mypy' to your dev dependencies. Add a [tool.mypy] section to pyproject.toml. Add 'mypy src/' to your CI step. Configure mypy to fail the build on any error.

My default mypy config is strict — strict = true. This enables all the strict-mode flags. You'll write more careful code; mypy will reward you with more catches.

Alternatives — pyright (Microsoft, used by VS Code's Pylance) is faster than mypy and roughly equivalent in catches. Some teams use both. For most teams, one is enough.

The rule I follow on personal projects — strict mypy, no #type: ignore in committed code. If I'm tempted to ignore, I'm doing something wrong. Fix the type, not the silence.

It is, unironically, the single most boring and most valuable habit you can adopt in a new Python project. Start now.
#Python#AI#100DaysOfCode#BuildInPublic#PythonProgramming#PythonTesting
POST 5 of 5 NightPythonRecap

Day 13 — types make Python serious

End of Day 13. One day left in week two, then we head into DSA territory.

What we covered.

Morning, the case for type hints. They don't run. They catch bugs anyway, give you better IDE support, document your intent, and integrate with frameworks that consume them at runtime. The cost is small. The benefit compounds.

Midday, the eight type primitives that cover 95% of typed Python. str, int, float, bool, list[T] / dict[K,V], Optional/None, Literal, Callable, Any (sparingly), TypedDict, Protocol. Learn these eight. The rest comes later when you're writing libraries, not applications.

Afternoon, @dataclass. The decorator that compresses 30-line boilerplate classes into 5-line declarations. Auto-generates __init__, __repr__, __eq__, __hash__ from the type-hinted fields. frozen=True for immutability and cleaner hashing.

Evening, the strongest single recommendation in Python project setup — add mypy to CI on day one. Strict mode. No 'we'll add types later'. Day one. The compound interest is enormous; the retroactive migration is brutal.

A week-two perspective check. We've now covered classes, inheritance, dunder methods, decorators, generators, context managers, type hints, and dataclasses. That's pretty much the complete 'modern Python' toolkit. By tomorrow's wrap, you'll be able to read most open-source AI/ML codebases without flinching at the language constructs.

Tomorrow, Day 14, errors and exceptions. The rules for catching them, when to define your own, and the 3.11+ ExceptionGroup pattern that handles parallel failures. Then we close week two.

See you in the morning.
#Python#AI#100DaysOfCode#BuildInPublic#PythonProgramming#PythonTyping