Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to subclass a dictionary so it supports generic type hints?

How can a dictionary be subclassed such that the subclass supports generic type hinting? It needs to behave like a dictionary in every way and support type hints of the keys and values. The subclass will add functions that access and manipulate the dictionary data. For example, it will have a valueat(self, idx:int) function that returns the dictionary value at a given index.

It doesn't require OrderedDict as its base class, but the dictionary does need to have a predictable order. Since OrderedDict maintains insertion order and supports type hints, it seems like a reasonable place to start. Here's what I tried:

from collections import OrderedDict

class ApplicationSpecificDict(OrderedDict[str, int]):
    ...

However, it fails with the error: TypeError: 'type' object is not subscriptable

Is this not supported in Python 3.7+, or am I missing something?

like image 524
Tom Jordan Avatar asked Apr 09 '20 01:04

Tom Jordan


People also ask

How do you set a value in a dictionary in a way that doesn't override existing values?

By using the setdefault() method, you can add items with new values only for new keys without changing the values for existing keys. This is useful when you don't want to change an existing item. This article describes the following contents.

Does Python enforce type hints?

Python will always remain a dynamically typed language. However, PEP 484 introduced type hints, which make it possible to also do static type checking of Python code. Unlike how types work in most other statically typed languages, type hints by themselves don't cause Python to enforce types.

How do you use type hints in Python?

Here's how you can add type hints to our function: Add a colon and a data type after each function parameter. Add an arrow ( -> ) and a data type after the function to specify the return data type.

How do I fetch values in a dictionary?

Use dict.get() to get the default value for non-existent keys. You can use the get() method of the dictionary ( dict ) to get any default value without an error if the key does not exist.


1 Answers

I posted on this question which yours may be a dupe of, but I will include it here as well because I found both of these questions when I was googling how to do this.

Basically, you need to use the typing Mapping generic This is the generic annotation that dict uses so you can define other types like MyDict[str, int].

How to:

import typing
from collections import OrderedDict

# these are generic type vars to tell mutable-mapping 
# to accept any type vars when creating a sub-type of your generic dict
_KT = typing.TypeVar("_KT") #  key type
_VT = typing.TypeVar("_VT") #  value type


# `typing.MutableMapping` requires you to implement certain functions like __getitem__
# You can get around this by just subclassing OrderedDict first.
# Note: The generic you're subclassing needs to come BEFORE
# the `typing.MutableMapping` subclass or accessing indices won't work.

class ApplicationSpecificDict(
        OrderedDict, 
        typing.MutableMapping[_KT, _VT]
):
    """Your special dict"""
    ...

# Now define the key, value types for sub-types of your dict
RequestDict = MyDict[str, typing.Tuple[str, str]]
ModelDict = MyDict[str, typing.Any]

Now use you custom types of your sub-typed dict:

from my_project.custom_typing import ApplicationSpecificDict #  Import your custom type

def make_request() -> ApplicationSpecificDict:
    request = ApplicationSpecificDict()
    request["test"] = ("sierra", "117")
    return request

print(make_request())

Will output as { "test": ("sierra", "117") }

like image 95
Alex Avatar answered Oct 07 '22 01:10

Alex