Tom*_*dan 10 python dictionary type-hinting python-3.x mypy
如何对字典进行子类化,以便子类支持泛型类型提示?它需要在各方面都像字典一样,并支持键和值的类型提示。该子类将添加访问和操作字典数据的函数。例如,它将有一个valueat(self, idx:int)函数返回给定索引处的字典值。
它不需要OrderedDict作为其基类,但字典确实需要具有可预测的顺序。由于OrderedDict维护插入顺序并支持类型提示,这似乎是一个合理的起点。这是我尝试过的:
from collections import OrderedDict
class ApplicationSpecificDict(OrderedDict[str, int]):
...
Run Code Online (Sandbox Code Playgroud)
但是,它失败并出现错误:
TypeError: 'type' object is not subscriptable
这在 Python 3.7+ 中不支持,还是我遗漏了什么?
我发布了这个问题,你可能被骗了,但我也会把它包括在这里,因为我在谷歌搜索如何做到这一点时发现了这两个问题。
基本上,您需要使用类型映射泛型
这是 dict 使用的泛型注释,因此您可以定义其他类型,例如MyDict[str, int].
如何:
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]
Run Code Online (Sandbox Code Playgroud)
现在使用您的子类型字典的自定义类型:
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())
Run Code Online (Sandbox Code Playgroud)
将输出为 { "test": ("sierra", "117") }
的打字包提供通用类,对应于非泛型类collections.abc和集合。这些泛型类可以用作基类来创建用户定义的泛型类,例如自定义泛型字典。
collections.abc:typing.AbstractSet(Sized, Collection[T_co])typing.Container(Generic[T_co])typing.Mapping(Sized, Collection[KT], Generic[VT_co])typing.MutableMapping(Mapping[KT, VT])typing.MutableSequence(Sequence[T])typing.MutableSet(AbstractSet[T])typing.Sequence(Reversible[T_co], Collection[T_co])collections:typing.DefaultDict(collections.defaultdict, MutableMapping[KT, VT])typing.OrderedDict(collections.OrderedDict, MutableMapping[KT, VT])typing.ChainMap(collections.ChainMap, MutableMapping[KT, VT])typing.Counter(collections.Counter, Dict[T, int])typing.Deque(deque, MutableSequence[T])实现自定义通用字典有很多选项。但是,重要的是要注意,除非用户定义的类显式继承自Mappingor MutableMapping,否则像mypy这样的静态类型检查器不会将该类视为映射。
from collections import abc # Used for isinstance check in `update()`.
from typing import Dict, Iterator, MutableMapping, TypeVar
KT = TypeVar('KT')
VT = TypeVar('VT')
class MyDict(MutableMapping[KT, VT]):
def __init__(self, dictionary=None, /, **kwargs) -> None:
self.data: Dict[KT, VT] = {}
if dictionary is not None:
self.update(dictionary)
if kwargs:
self.update(kwargs)
def __contains__(self, key: KT) -> bool:
return key in self.data
def __delitem__(self, key: KT) -> None:
del self.data[key]
def __getitem__(self, key: KT) -> VT:
if key in self.data:
return self.data[key]
raise KeyError(key)
def __len__(self) -> int:
return len(self.data)
def __iter__(self) -> Iterator[KT]:
return iter(self.data)
def __setitem__(self, key: KT, value: VT) -> None:
self.data[key] = value
@classmethod
def fromkeys(cls, iterable: Iterable[KT], value: VT) -> "MyDict":
"""Create a new dictionary with keys from `iterable` and values set
to `value`.
Args:
iterable: A collection of keys.
value: The default value. All of the values refer to just a single
instance, so it generally does not make sense for `value` to be a
mutable object such as an empty list. To get distinct values, use
a dict comprehension instead.
Returns:
A new instance of MyDict.
"""
d = cls()
for key in iterable:
d[key] = value
return d
def update(self, other=(), /, **kwds) -> None:
"""Updates the dictionary from an iterable or mapping object."""
if isinstance(other, abc.Mapping):
for key in other:
self.data[key] = other[key]
elif hasattr(other, "keys"):
for key in other.keys():
self.data[key] = other[key]
else:
for key, value in other:
self.data[key] = value
for key, value in kwds.items():
self.data[key] = value
Run Code Online (Sandbox Code Playgroud)