python-3.x 输入错误“Optional[Dict[str,str]]”的项“None”没有属性“get”

2ekbmq32  于 2023-01-22  发布在  Python
关注(0)|答案(2)|浏览(389)

下面的代码运行良好-

from typing import List, Dict, Any
def func1(input_list: List[Dict[str, Dict[str, str]]],):
    for config_dict in input_list:
        table_1 = ".".join(
            (
                config_dict.get("src").get("db"), # line 6
                config_dict.get("src").get("schema"), # line 7
                config_dict.get("src").get("table_name"), # line 8
            )
        )
        print(table_1)

if __name__ == "__main__":
    func1(
        [
            {
                "src": {
                    "db": "db",
                    "schema": "abc",
                    "table_name": "bcd",
                },
                "trgt": {
                    "db": "dd",
                    "schema": "asd",
                    "table_name": "fds",

                },
            }
        ]
    )

但是当我在上面运行mypy时,我得到了以下错误

> mypy abc.py
abc.py:6: error: Item "None" of "Optional[Dict[str, str]]" has no attribute "get"  [union-attr]
abc.py:7: error: Item "None" of "Optional[Dict[str, str]]" has no attribute "get"  [union-attr]
abc.py:8: error: Item "None" of "Optional[Dict[str, str]]" has no attribute "get"  [union-attr]

我错过了什么吗?我想保持input_list结构不变。

mypy --version
mypy 0.991 (compiled: yes)
7fhtutme

7fhtutme1#

dict.get的返回值具有Optional[Dict[str, str]]类型,这意味着它 * 可以 * 在运行时返回None,所以不能无条件地假设返回值具有get方法,这就是mypy为您捕获的错误。
您可以通过首先 * 检查 * 返回值是否为dict来修复此问题。

def func1(input_list: List[Dict[str, Dict[str, str]]],):
    for config_dict in input_list:
        s = config_dict.get("src")
        if s is None:
            continue

        table_1 = ".".join(
            (
                s.get("db"), # line 6
                s.get("schema"), # line 7
                s.get("table_name"), # line 8
            )
        )
        print(table_1)

这是mypy可以执行类型收缩的一个例子,它假设如果在s is None之后执行,则s不是None,因此它的类型可以从Optional[Dict[str,str]]“收缩”(更改为更具体的子类型)到Dict[str, str]
您也可以停止使用get,而改用__getitem__。在这里,键查找失败是由异常指示的,而不是由特定的返回值指示的,因此类型收缩也会被执行。(只有在不引发异常时才会到达以下代码)

def func1(input_list: List[Dict[str, Dict[str, str]]],):
    for config_dict in input_list:
        s = config_db["src"]
        table_1 = ".".join(
            (
                s.get("db"), # line 6
                s.get("schema"), # line 7
                s.get("table_name"), # line 8
            )
        )
        print(table_1)
s4n0splo

s4n0splo2#

您的函数注解太宽泛:它允许dict列表中的键与函数的期望值不匹配,可以使用typing.TypeDict来代替Dict[str, str],更具体一些,你仍然需要检查config_dict.get("src")返回的是TableConfig,而不是None,但是你可以假设TableConfig的值 * 确实 * 有你想要使用的三个键。(正如我的另一个答案一样,我将切换到config_dict["src"],让查找通过异常而不是特殊的返回值失败。)

from typing import List, Dict, Any, TypedDict

class TableConfig(TypedDict):
    db: str
    schema: str
    table_name: str

def func1(input_list: List[Dict[str, TableConfig]],):
    for config_dict in input_list:
        table_1 = ".".join(
            (
                config_dict["src"]["db"],
                config_dict["src"]["schema"],
                config_dict["src"]["table_name"],
            )
        )
        print(table_1)

如果"src""target"不是任意键,而是模式的一部分,您甚至可以更具体地说明。

class Mover(TypedDict):
    src: TableConfig
    target: TableConfig

def func1(input_list: List[Mover],):
    for config_dict in input_list:
        table_1 = ".".join(
            (
                config_dict["src"]["db"],
                config_dict["src"]["schema"],
                config_dict["src"]["table_name"],
            )
        )
        print(table_1)

相关问题