# !mark(15:16)
Source: https://speakeasy.com/blog/pydantic-vs-dataclasses

import { Callout, Table } from "@/mdx/components";

<Callout title="Tip" type="default">
  A massive thank you to [Sydney Runkle](https://x.com/sydneyrunkle) from the
  [Pydantic](https://pydantic.dev/) team for her invaluable feedback and
  suggestions on this post!!
</Callout>

Python's dynamic typing is one of its greatest strengths. It is the language developers use to get things done without getting bogged down by type definitions and boilerplate code. When prototyping, you don't have time to think about unions, generics, or polymorphism - close your eyes, trust the interpreter to guess your variable's type, and then start working on the next feature.

That is, until your prototype takes off and your logs are littered with `TypeError: 'NoneType' object is not iterable` or `TypeError: unsupported operand type(s) for /: 'str' and 'int'`. You might blame the users for adding units in the amount field, or the frontend devs for posting `null` instead of `[]`. So you fix the bug with another `if` statement, a `try` block, or the tenth validation function you've written this week. No time for reflection, just keep shipping, right? The ball of twine must grow.

We all know there is a better way. Python has had type annotations for years, and data classes and typed dictionaries allow us to document the shapes of the objects we expect.

Pydantic is the most comprehensive solution available to enforce type safety and data validation in Python, which is why we chose it for our SDKs at Speakeasy.

In this post we'll run through how we got to this conclusion. We'll detail the history of type safety in Python and explain the differences between: type annotations, data classes, TypedDicts, and finally, Pydantic.

## If It Walks Like a Duck and It Quacks Like a Duck, Then It Must Be a Duck

Python is a [duck-typed language](https://docs.python.org/3/glossary.html#term-duck-typing). In a duck-typed language, an object's type is determined by its behavior at runtime, based on the parts of the object that are actually used. Duck-typing makes it easier to write generic code that works with different types of objects.

If your code expects a `Duck` object to make it quack, Python doesn't care if the object is a `Mallard` or a `RubberDuck`. From Python's perspective, anything with a `quack` method is a `Duck`:

```python
class Duck:
    def quack(self):
        print("Quack!")

class Mallard:
    def quack(self):
        print("Quack!")

def make_duck_quack(duck):
    duck.quack()

make_duck_quack(Duck()) # prints "Quack!"
make_duck_quack(Mallard()) # prints "Quack!"
```

This code runs without errors, even though `make_duck_quack` expects a `Duck` object in our mental model, and we pass it a `Mallard` object. The `Mallard` object has a `quack` method, so it behaves like a `Duck` object.

One of the reasons for Python's popularity is its flexibility. You can write generic and reusable code without worrying about the specific object types.

But this flexibility comes at a cost. If you pass the wrong type of object to a function you'll only find out at runtime, leading to bugs that are difficult to track down.

This was the motivation behind developing type annotations.

## Type Annotations

Type annotations were introduced in Python 3.5 to add optional type hints to your code ([PEP 484](https://www.python.org/dev/peps/pep-0484/)). Type hints can help you catch bugs while you are still writing your code by telling you when you pass the wrong type of object to a function.

<Callout title="TIP" type="info">
To make the most of these type hints, many developers use type checkers. Type checkers are tools that analyze your Python code without running it, looking for potential type-related errors. One popular type checker is [Pylance](https://marketplace.visualstudio.com/items?itemName=ms-python.vscode-pylance), a Visual Studio Code Extension that checks your Python code for type mismatches and shows you hints in your IDE.

If you're not using VS Code, [Pyright](https://github.com/microsoft/pyright/tree/main) has similar functionality and can be run from the [command line](https://microsoft.github.io/pyright/#/command-line) or as an [extension](https://microsoft.github.io/pyright/#/installation) to many text editors.

</Callout>

Here's how you can add type hints to the `make_duck_quack` function:

```python

class Duck:
    def quack(self):
        print("Quack!")


class RubberDuck:
    def quack(self):
        print("Quack!")


def make_duck_quack(duck: Duck):
    duck.quack()

make_duck_quack(Duck()) # prints "Quack!"
make_duck_quack(RubberDuck())