python - 在 python 3.6 中通过属性表示法表示 dict 键时的类型提示
问题描述
给出Accessing dict keys like a attribute 中第一个答案的示例?:
class AttrDict(dict):
def __init__(self, *args, **kwargs):
super(AttrDict, self).__init__(*args, **kwargs)
self.__dict__ = self
和一个返回的函数:
def dict_to_attrdict(somedict):
return AttrDict(**somedict)
分配为:
data = dict_to_attrdict(mydict)
在给定以下约束的情况下,为将通过 mypy 检查的类和函数添加类型提示的正确方法是什么:
- dict 键将始终是
str
- dict 值必须是动态的并由它们表示,
Any
因为它们会有所不同,我不希望单独键入每个值,即 somestr
,List[dict[str, List]]
,Dict[str, str]
,Dict[str, List]
解决方案
您可以通过执行以下操作使类和函数定义本身进行类型检查:
from typing import Dict, Any
class AttrDict(Dict[str, Any]):
def __init__(self, *args: Any, **kwargs: Any) -> None:
super(AttrDict, self).__init__(*args, **kwargs)
self.__dict__ = self
def dict_to_attrdict(some: Dict[str, Any]) -> AttrDict:
return AttrDict(**some)
继承自Dict[X, Y]
与仅dict
在运行时继承自没有什么不同,但它为 mypy 提供了所需的额外元数据。
但是,您实际上无法以AttrDict
类型安全的方式使用 的实例:mypy 将始终将诸如错误之类的事情标记my_attrdict.foo
为错误。
这是因为不可能静态地确定AttrDict
在所有情况下会出现哪些字段——mypy 不知道里面到底有什么AttrDict
。而且由于 mypy 无法判断做类似my_attrdict.foo
的事情实际上是否安全,它倾向于保守的一面,并决定认为这是不安全的。
您有两种不同的选择来解决这个问题。首先,如果您真的想尽可能保持AttrDict
动态,您可以告诉 mypy 假设该类型是任意动态类型,如下所示:
from typing import Dict, Any, TYPE_CHECKING
if TYPE_CHECKING:
AttrDict = Any
else:
class AttrDict(dict):
def __init__(self, *args, **kwargs) -> None:
super(AttrDict, self).__init__(*args, **kwargs)
self.__dict__ = self
def dict_to_attrdict(some: Dict[str, Any]) -> AttrDict:
return AttrDict(**some)
TYPE_CHECKING
是一个在运行时始终为 False 的值,但 mypy 将其视为始终为 True。最终效果是 mypy 将只考虑 if/else 的“if”分支,并忽略“else”分支中的任何内容:我们现在已经教 mypyAttrDict
是 : 的类型别名Any
完全等同于Any
. 然而,在运行时,我们总是落入“else”分支并像以前一样定义类。
这种方法的主要缺点是我们实际上没有从使用静态类型中获得任何价值。我们可以增加一点安全性,dict_to_attrdict
因为我们现在可以强制密钥必须是字符串,但仅此而已。
第二种选择是利用 mypy 的优势并重写您的代码以实际使用类。因此,我们将摆脱AttrDict
并实际使用设置字段的类。
这可以让 mypy 了解存在哪些字段,它们的类型是什么等。这需要更多的前期工作,但好处是 mypy 能够为您的代码的正确性提供更强的保证。
如果您发现实际上定义一堆带字段的类很乏味,请尝试使用新的“dataclasses”模块(如果您使用的是 Python 3.7)或第 3 方“attrs”模块。我相信 mypy 最近增加了对两者的支持。
不过,如果您想使用数据类,您可能必须等到 mypy 0.620 在即将到来的星期二发布——我不记得该功能是在 mypy 0.600 还是 mypy 0.610 中使用的。
推荐阅读
- xamarin - 使用 Xamarin 表单自定义 ListView
- java - 如何在@PostConstruct 之前调用@BeforeMethod 块
- c# - CSV 更新/修改
- javascript - 为什么我无法将 ${} 放入 javascript 代码中?
- java - Java Spring MVC 应用程序 - 如何解决此警告?
- flutter - 从主屏幕返回时隐藏指纹屏幕
- android - 如何修复 Android XML 布局中的“错误膨胀类 EditText”
- ajax - 在 Slickgrid 中突出显示搜索关键字
- python - 使用 Saver 参数“keep_checkpoint_every_n_hours”的正确方法是什么?
- c++ - 如何将 c++ 中的后端编写器与 tcl/tck 用户界面连接起来?