I often use dict
to group and namespace related data. Two drawbacks are:
- I cannot type-hint individual entries (e.g.
x['s']: str = ''
). Accessing union-typed values (e.g.x: dict[str, str | None] = {}
) later needsassert
statements to please mypy. - Spelling entries is verbose. Values mapped to
str
keys need four extra characters (i.e.['']
); attributes only need one (i.e..
).
I've considered types.SimpleNamespace
. However, like with classes, I run into this mypy error:
import types
x = types.SimpleNamespace()
x.s: str = ''
# 3 col 2 error| Type cannot be declared in assignment to non-self attribute [python/mypy]
- Is there a way to type-hint attributes added after instantiation?
- If not, what other structures should I consider? As with
dict
and unlikecollections.namedtuple
, I require mutability.
CodePudding user response:
There is no way to type-hint attributes that are not defined inside class body or __init__
.
You need to declare some sort of structure with known fields or keys and then use it. You have a whole bunch of options. First things to consider (as most similar to your existing attempt) are TypedDict
and dataclass
. TypedDict
does no runtime validation and is just a plain dictionary during code execution (no key/value restrictions apply). dataclass
will create an __init__
for you, but you'll be able to set any attributes later (without annotation, invisible for mypy
). With dataclass(slots=True)
, it will be impossible.
Let me show some examples:
from typing import TypedDict
class MyStructure(TypedDict):
foo: str
data: MyStructure = {'foo': 'bar'}
reveal_type(data['foo']) # N: revealed type is "builtins.str"
data['foo'] = 'baz' # OK, mutable
data['foo'] = 1 # E: Value of "foo" has incompatible type "int"; expected "str" [typeddict-item]
data['bar'] # E: TypedDict "MyStructure" has no key "bar" [typeddict-item]
# Second option
from dataclasses import dataclass
@dataclass
class MyStructure2:
foo: str
data2 = MyStructure2(foo='bar')
reveal_type(data2.foo) # N: Revealed type is "builtins.str"
data2.foo = 'baz' # OK, mutable
data2.foo = 1 # E: Incompatible types in assignment (expression has type "int", variable has type "str") [assignment]
data2.bar # E: "MyStructure2" has no attribute "bar" [attr-defined]
Here's a playground link.