DEV Community

Discussion on: TypedDict vs dataclasses in Python — Epic typing BATTLE!

Collapse
 
travisjungroth profile image
Travis Jungroth • Edited

There are a few really important things in this post that are incorrect or misleading. There seems to be a fundamental misunderstanding about duck typing and static type checking.

TypedDict didn't bring duck typing to Python. Python was already the classic example of duck typing. You even linked to a Python lesson about it that doesn't include TypeDict.

Two Persons with the same attributes not being equal isn't because Python isn't duck typed (since it is). It's because the default for == (the __eq__ method) is just an identity check. You're free to define that method however you want for your classes. And on dataclasses, you could just pass eq=True to the decorator and it will check that all the attributes match and the types are the same.

Duck typing is the idea that a type is just defined by its method and attributes. Like:

def first_initial(thing):
    return thing.name[0]
Enter fullscreen mode Exit fullscreen mode

That will work on a Person or a Comet, since they both have the name attribute.

You example about cast is misleading. cast doesn't change the return value. It's a way to tell the type checker that you know something it doesn't. source

And the example about switching to using the Person constructor is just wrong. Passing a str instead of an int will not cause a runtime error, unless you also have installed some extra runtime typing package. Python type hinting is static. Check for yourself by running this:

from typing import Optional
from dataclasses import dataclass

@dataclass
class Person:
    name: str
    age: Optional[int] = None

def to_person(d: dict) -> Person:
    # Fixed the parens
    if ('name' in d) and isinstance(d['name'], str) and (('age' not in d) or (('age' in d) and (isinstance(d['age'], str)))):
        return Person(**d)  # fixed the variable
    raise ValueError('d is not a Person')

to_person({'name': 'Abraham', 'age': '100'})  # Works fine, no error
Enter fullscreen mode Exit fullscreen mode

The fact that you've got isinstance(r, Reference) all over your codebase is a hint that that the relationship between these types or your dependance on it is broken. If a function is able to get either, then it should be able to use either and let the instance handle the difference (polymorphism and/or duck typing).

You did end up at a better place, with dataclasses over TypedDicts. It sounds like you could also make good use of Protocols since what you seem to want is static duck typing, anyway.