How to default dict in typing.NamedTuple
?
#1327
-
I have this class: from typing import NamedTuple
class Event(NamedTuple):
data: dict I would like to add a default What's the recommended way to do it? Is there something like Thanks! |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 1 reply
-
This isn't supported. I would recommend using a dataclass if you need functionality that goes beyond what |
Beta Was this translation helpful? Give feedback.
-
@JelleZijlstra is correct that a dataclass is often the way to go if you need some more advanced functionality. However, there are a few workarounds in this situation, if you need to stick with a (1) Use an alternative constructor Document that the from typing import NamedTuple, TypeVar
S = TypeVar("S", bound="Event")
class Event(NamedTuple):
data: dict
@classmethod
def make(cls: type[S], data: dict | None = None) -> S:
if data is None:
data = {}
return cls(data) (2) Use a It's not possible to override from typing import NamedTuple, TypeVar
S = TypeVar("S", bound="Event")
class Event(NamedTuple("Event", [("data", dict)])):
__slots__ = ()
def __new__(cls: type[S], data: dict | None = None) -> S:
if data is None:
data = {}
return super().__new__(cls, data) (3) More elaborate solutions If you want to be fancy, it's possible to create a custom decorator like this (adapted from my StackOverflow answer here): from typing import NamedTuple, Callable, TypeVar, Any, cast
from functools import wraps
T = TypeVar('T')
def default_factory(**factory_kw: Callable[[], Any]) -> Callable[[type[T]], type[T]]:
def wrapper(wcls: type[T], /) -> type[T]:
@wraps(wcls.__new__)
def __new__(cls: type[T], /, **kwargs: Any) -> T:
for key, factory in factory_kw.items():
kwargs.setdefault(key, factory())
new = super(cls, cls).__new__(cls, **kwargs) # type: ignore[misc]
# This call to cast() is necessary if you run MyPy with the --strict argument
return cast(T, new)
cls_name = wcls.__name__
wcls.__name__ = wcls.__qualname__ = f'_{cls_name}'
return type(cls_name, (wcls, ), {'__new__': __new__, '__slots__': ()})
return wrapper
@default_factory(data=dict)
class Event(NamedTuple):
# You do not *need* to have the default value in the class body,
# but it makes MyPy a lot happier
data: dict = {}
e = Event()
e.data["foo"] = "bar"
e2 = Event()
e3 = Event(data={1: 2})
print(f"{e.data=}") # prints "e.data={'foo': 'bar'}"
print(f"{e2.data=}") # prints "e2.data={}"
print(f"{e3.data=}") # prints "e3.data={1: 2}" |
Beta Was this translation helpful? Give feedback.
This isn't supported. I would recommend using a dataclass if you need functionality that goes beyond what
typing.NamedTuple
provides.