mypy:“联盟项目没有属性”错误

问题描述 投票:0回答:1

尝试学习在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 进行测试)

python python-typing mypy
1个回答
13
投票

您的签名

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

表示

file
参数可以是
str
TextIO
,之后在函数体中您尝试访问
.read
对象的
file
属性,但如果
file
str
则有没有这样的属性,因此出现错误。

这里至少有 3 种可能性:

  • 不支持
    file
    str
    类型的情况,并将
    Union[str, TextIO]
    替换为
    TextIO
  • 使用函数体中的

    isinstance
    内置添加显式类型检查,如

    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
装饰器进行改进:简而言之,这将允许我们定义一个泛型函数并使用名称
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
会引发名称冲突错误。

© www.soinside.com 2019 - 2024. All rights reserved.