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 needsassertstatements to please mypy. - Spelling entries is verbose. Values mapped to
strkeys 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
dictand 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.
