mypy: "Item of Union has no attribute" 错误
mypy: "Item of Union has no attribute" error
正在尝试学习在 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 测试)
您的签名
def myfunc_error(file: Union[str, TextIO]):
...
说 file
参数可以是 str
或 TextIO
,之后在函数体中你试图访问 file
对象的 .read
属性, 但如果 file
是 str
则没有这样的属性因此错误。
这里至少有 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: in short this will allow us to define a 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
会产生一个名字冲突错误。
正在尝试学习在 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 测试)
您的签名
def myfunc_error(file: Union[str, TextIO]):
...
说 file
参数可以是 str
或 TextIO
,之后在函数体中你试图访问 file
对象的 .read
属性, 但如果 file
是 str
则没有这样的属性因此错误。
这里至少有 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: in short this will allow us to define a 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
会产生一个名字冲突错误。