Kalanamith
Kalanamith

Reputation: 20648

How to add a typing declaration for a dict with different types as values in python

I have a dictionary as follows

my_dict = {
   "key_1": "value_1",
   "key_2": {
       "key_1": True,
           "key_2": 1200
       }
   "key_3": True,
}

and in my class

@dataclass
class TestClass:
    my_dict: typing.Dict[{str, str}, {str, typing.Dict[{str, bool}, {str, int}]}]

The above declaration is incorrect.

If I want to add typing for my_dict what it should be and how to write the structure since I am having different types as values?

Upvotes: 4

Views: 2797

Answers (3)

Sergei Zobov
Sergei Zobov

Reputation: 409

Since Python 3.8, we have TypedDict that could very well solve what you need:

from dataclasses import dataclass
from typing import TypedDict

class NestedDictType(TypedDict):
    key_1: bool
    key_2: int

class TopLevelDictType(TypedDict):
    key_1: str
    key_2: NestedDictType
    key_3: bool

@dataclass
class TestClass:
    my_dict: TopLevelDictType

Upvotes: 0

Todd
Todd

Reputation: 5385

I'll take a stab it it. So, the type of the values looks to be pretty distinct and easy to determine at runtime. The code that accesses the dictionary data and performs some action on it dependent on its type can use instanceof() in an if/elif/else block.

def some_method(self, key):
    val = self.my_dict[key]
    if isinstance(val, str): # fixed from instanceof to isinstance...
        print val
    elif isinstance(val, dict):
        print "it was a dictionary"
    else:
        print "Guess it must have been an int or bool."

or you could test for type like so: if type(val) is str: dosomething()

Upvotes: 1

Bailey Parker
Bailey Parker

Reputation: 15905

You want to use a Union as the value for the dictionary:

from typing import Dict, Union

@dataclass
class TestClass:
    my_dict: Dict[str, Union[str, bool, int]]

The union informs the typechecker that values in the dictionary must be either strs, bools, or ints. When getting values, you'll want to use isinstance to determine what to do with a value:

if isinstance(self.my_dict['a'], str):
    return self.my_dict['a'].encode('utf-8')
else isinstance(self.my_dict['a'], bool):
    return not self.my_dict['a']
else:
    return self.my_dict['a'] / 10

If you know that a key will contain a specific type, you can avoid the typechecker's complaints by using cast:

from typing import cast

value = cast(bool, self.my_dict['some_bool'])

Upvotes: 4

Related Questions