在python 3.6中,通过属性表示法表示dict键时键入提示

在python 3.6中,通过属性表示法表示dict键时键入提示,python,python-3.x,types,mypy,Python,Python 3.x,Types,Mypy,给出了中第一个答案的示例: 以及一个函数,该函数返回: def dict_to_attrdict(somedict): return AttrDict(**somedict) 分配为: data = dict_to_attrdict(mydict) 在以下约束条件下,为将通过mypy检查的类和函数添加类型提示的正确方法是什么: dict键将始终为str dict值必须是动态的,并由Any表示,因为它们不同,我不希望单独键入,即一些str,List[dict[str,List]],d

给出了中第一个答案的示例:

以及一个函数,该函数返回:

def dict_to_attrdict(somedict):
    return AttrDict(**somedict)
分配为:

data = dict_to_attrdict(mydict)
在以下约束条件下,为将通过mypy检查的类和函数添加类型提示的正确方法是什么:

  • dict键将始终为
    str
  • dict值必须是动态的,并由
    Any
    表示,因为它们不同,我不希望单独键入,即一些
    str
    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无法判断是否执行诸如<代码> MyAutoDist.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只考虑I/ER的“if”分支,而忽略“否则”分支中的任何内容:我们现在已经教MyPy,<代码> AddiDAT< /COD>是<代码>的任何别名>任何:完全等同于<代码>任何< /代码>。然而,在运行时,我们总是落入“else”分支,并像以前一样定义类

这种方法的主要缺点是,我们实际上没有从使用静态类型中获得任何价值。我们可以为dict\u to\u attrdict增加一点安全性,因为我们现在可以强制执行键必须是字符串,但仅此而已

第二种选择是利用mypy的优势,重写代码以实际使用类。因此,我们将去掉AttrDict,实际使用设置字段的类

这可以让mypy了解存在哪些字段,它们的类型是什么,等等。这需要更多的前期工作,但好处是mypy能够为您的代码的正确性提供更有力的保证

如果您发现实际定义一组带有字段的类很乏味,那么请尝试使用新的“dataclasses”模块(如果您使用的是Python 3.7)或第三方的“attrs”模块。我相信mypy最近增加了对两者的支持


如果您想使用数据类,您可能需要等到mypy 0.620在即将到来的周二发布——我不记得这一特性是否出现在mypy 0.600或mypy 0.610中。

谢谢您的解释!最后我选择了一种稍微不同的方法。我用一个递归设置该类并处理嵌套列表的类替换了该类,但仍然选择了“Dict[str,Any]”方法,就像上面的答案一样。然后,我创建了一个NamedTuple类,该类定义了我实际上想要从dict中使用的嵌套结构的类型。这样,属性可以设置它们的类型,保持了我在代码的其他部分首先想要的灵活性,但不会破坏静态类型的目的。
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)