与TextIO类型联合的mypy属性错误

时间:2018-08-10 08:25:54

标签: python python-3.x type-hinting mypy

尝试学习在Python中键入提示。鉴于这两个功能:

from typing import Union, TextIO


def myfunc_ok(file: TextIO):
    mydump = file.read()
    print(mydump)


def myfunc_error(file: Union[str, TextIO]):
    mydump = file.read()
    print(mydump)

第一个对mypy没问题,但它抱怨第二个有错误

Item "str" of "Union[str, TextIO]" has no attribute "read"

在这种情况下,我是否使用了不正确的类型提示? (将python3.7和mypy 0.610一起使用,也对py3.6进行了测试)

1 个答案:

答案 0 :(得分:2)

您的签名

def myfunc_error(file: Union[str, TextIO]):
    ...

file参数可以是strTextIO,然后在函数主体中尝试访问.read对象的file属性,但是如果filestr,则没有这样的属性,因此会出错。

您在这里至少有3种可能性:

  • 不支持file类型为str的情况,而将Union[str, TextIO]替换为TextIO
  • 在函数体中使用isinstance built-in添加显式类型检查,例如

    import io
    ...
    def myfunc_error(file: Union[str, TextIO]):
        if isinstance(file, io.TextIOWrapper):
            mydump = file.read()
        else:
            # assuming ``file`` is a required object already
            mydump = file
        print(mydump)
    

    从长远来看,这可能变得难以维持

  • 为给定任务编写2种不同的功能:一种用于str参数,另一种用于TextIO参数,例如

    def myfunc_error_str_version(file: str):
        mydump = file
        print(mydump)
    
    def myfunc_error_text_io_version(file: TextIO):
        mydump = file.read()
        print(mydump)
    

    这可能会导致很多命名问题(但这取决于用例)

可以使用functools.singledispatch decorator来改进最后一种方法:简而言之,这将使我们能够定义generic function并使用名称myfunc_error并根据第一个位置参数的类型调用重载(在我们的例子中为file

import io
from functools import singledispatch
from typing import TextIO


@singledispatch
def myfunc_error(file: str):
    mydump = file
    print(mydump)

# using ``typing.TextIO`` will not work because it's just an interface for type annotations,
# "real" types are located at ``io`` module
@myfunc_error.register(io.TextIOWrapper)
def _(file: TextIO):
    mydump = file.read()
    print(mydump)

注意:除了_以外,我们可以使用我们想要的任何名称代替myfunc_error,因为后者mypy会引发名称冲突错误。