我正在处理一个来自数据库的对象,该数据库是
dict
的子类,但它没有以可以接受任何类型参数的方式定义。在我下面的示例中,我将此类型命名为SpecialDict
,出于我正在做的事情的目的,我只想将其转换为标准dict
.
现在,我正在创建一个函数,将这个
SpecialDict
的实例转换为标准dict
- 然而,这个SpecialDict
(以及后续的dict
)可以任意嵌套但有一定的限制:
str
SpecialDict
list
int
和str
真实的例子是以同样的方式设置的,但是允许的原子类型不仅仅是
int
和str
,但这不应该影响问题。
鉴于上述情况,上述函数的简单递归实现将是:
SUPPORTED_CONTAINER_TYPES = (dict, list)
SUPPORTED_ATOMIC_TYPES = (str, int)
class SpecialDict(dict):
pass
def converter(container):
if isinstance(container, dict):
dict_ = {}
for key, value in container.items():
if isinstance(value, SUPPORTED_CONTAINER_TYPES):
dict_[key] = converter(container=value)
elif isinstance(value, SUPPORTED_ATOMIC_TYPES):
dict_[key] = value
else:
raise TypeError(f"{type(value)} is not supported.")
return dict_
if isinstance(container, list):
datastore_list = []
for item in container:
if isinstance(item, SUPPORTED_CONTAINER_TYPES):
datastore_list.append(converter(container=item))
elif isinstance(item, SUPPORTED_ATOMIC_TYPES):
datastore_list.append(item)
else:
raise TypeError(f"{type(item)} is not supported.")
return datastore_list
raise TypeError(f"{type(container)} is not supported.")
本质上,我们递归地遍历
SpecialDict
并填充一个新的dict
。问题是我无法正确输入此内容。
该函数可以将
list
、dict
或 SpecialDict
(虽然也是 dict
子类)作为输入,并返回 list
或 dict
。
但是,由于这些都可以任意包含它们自己或其他容器类型,我会尝试按照这些行递归地定义类型:
from typing import Union
SUPPORTED_CONTAINER_TYPES = (dict, list)
SUPPORTED_ATOMIC_TYPES = (str, int)
class SpecialDict(dict):
pass
MyList = list[Union[str, int, "MyList", "MyDict", SpecialDict]]
MyDict = dict[str, Union[str, int, MyList, "MyDict", SpecialDict]]
MyListWithoutSpecialDict = list[Union[str, int, "MyList", "MyDict"]]
MyDictWithoutSpecialDict = dict[str, Union[str, int, MyList, "MyDict"]]
def converter(container: MyList | MyDict) -> MyListWithoutSpecialDict | MyDictWithoutSpecialDict:
if isinstance(container, dict):
dict_ = {}
for key, value in container.items():
if isinstance(value, SUPPORTED_CONTAINER_TYPES):
dict_[key] = converter(container=value)
elif isinstance(value, SUPPORTED_ATOMIC_TYPES):
dict_[key] = value
else:
raise TypeError(f"{type(value)} is not supported.")
return dict_
if isinstance(container, list):
datastore_list = []
for item in container:
if isinstance(item, SUPPORTED_CONTAINER_TYPES):
datastore_list.append(converter(container=item))
elif isinstance(item, SUPPORTED_ATOMIC_TYPES):
datastore_list.append(item)
else:
raise TypeError(f"{type(item)} is not supported.")
return datastore_list
raise TypeError(f"{type(container)} is not supported.")
但这给出了一些 mypy 错误,而且似乎也没有真正捕捉到我正在尝试做的事情。将函数分解成另一个执行某些类型强制的函数可能会有所帮助,但在一天结束时,我总是会留下这个最外层函数的类型提示,我不确定如何解决.
有什么建议吗?
你想要的是类型级别的映射
MyList -> MyListWithoutSpecialDict
MyDict -> MyDictWithoutSpecialDict
然后你可以定义类似的东西
# N.B. MyList and MyDict need to be real types, not type hints
T = TypeVar('T', MyList, MyDict)
def converter(container: T) -> TF[T]:
...
在哪里
TF
提供映射。
我认为 Python 的类型提示还不支持这样的概念。
请注意,您在
converter
上的类型提示隐式定义了一个类型
Callable[[Union[MyList, MyDict]],
Union[MyListWithoutSpecialDict, MyDictWithoutSpecialDict]]
和你要的类型不一样,即
Union[
Callable[[MyList], MyListWithoutSpecialDict],
Callable[[MyDict], MyDictWithoutSpecialDict]
]
如果语法允许,你可以这样写
converter: Union[Callable[...], Callable[...]] # per above
def converter(container):
...
假设的类型映射
TF
是让您在 Python 的语法中定义等效类型的部分
Callable[[T], TF[T]]