如何验证 TextIO 参数?

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

我刚刚开始接受 Python 类型提示,但我很困惑如何为以下函数签名实现参数验证:

def read_file(file: Union[str, PathLike, TextIO]) -> str:

我对 pythonic 实现的最初尝试如下:

def read_file(file: Union[str, PathLike, TextIO]) -> str:
    try:
        with open(file) as fileIO:
            return read_file(fileIO)
    except TypeError:
        return file.read()

虽然这看起来像是

read_file
的完全有效的 Pythonic 实现,但类型检查器对此进行了全面检查。这是可以理解的,因为
open
不接受除
TypeIO
之外的任何可能类型,并且没有任何东西可以缩小类型范围(尽管令人惊讶的是
except TypeError:
没有被考虑)。

所以我放弃了“请求原谅”,而是尝试明确地检查论点:

def read_file(file: Union[str, PathLike, TextIO]) -> str:
    if isinstance(file, TextIO):
        return file.read()
    else:
        with open(file) as fileIO:
            return read_file(fileIO)

这扭转了整个逻辑并检查文件是否为

TextIO
,因此类型检查器很高兴。问题是,这实际上在运行时没有任何意义,因为
TextIO
实际上并不是您可以检查的基类,而是仅用于类型提示的类型。

现在我开始感到非常困惑,因为我意识到我实际上不知道如何在运行时检查某些东西是否是

TextIO
。我挖了各种兔子洞来检查变量是类似路径还是类似文件,但这一切感觉就像我在这里遗漏了一些基本的东西。我的意思是,如果类型检查器可以提前知道某些东西是
TextIO
那么在实现中缩小类型范围怎么会很难呢?

这一定是在各种库中完成的事情,但我发现大多数实现都使用模糊检查

read
和可迭代等。也许是为了向后兼容,但我的目标是 python 3.9+,所以希望通过现在可能有更好的解决方案。

注意:作为澄清,给出评论和现有答案。我不是问打开文本文件的正确实现是什么。我也不是问如何使用类型提示一般来说:我知道你应该使用类型缩小来让类型检查器满意。

我的问题是:您可以在代码中使用哪些函数或表达式来专门对

TextIO
类型提示进行类型缩小,以便编译器乐意丢弃变量的该类型?
try.. except
不起作用,检查值是否为
TextIOBase
TextIO
的实例也不起作用。

python type-hinting python-typing
2个回答
0
投票

您可以测试该对象是否是

io.TextIOBase
的实例:

import io

with open(__file__) as file:
    print(isinstance(file, io.TextIOBase))

输出:

True

但是请注意,您在大多数实际应用程序中看到对属性

read
等的模糊检查的原因是,对于任何给定的情况,通常只需要
typing.IO
中定义的 20 个抽象方法中的少数几个。应用。这就是为什么接受文件对象的函数的文档通常将它们称为file-like对象,因为它们通常只需要对象中的一些方法,例如
read
tell
readline
等,并且许多类似文件的类的实现并不是真正继承自
io.IOBase
,因此鸭子类型检查通常优于严格的
isinstance
检查。


0
投票

使用类型缩小,非常仔细地注意如何在类型缩小块的子句中排序类型测试。具体来说,对于难以测试的类型,使用“catch-all”子句来捕获类型,即:

以下示例可以在 mypy-play.netpyright-play.net 上查看:

import os
import typing as t

def read_file(file: str | os.PathLike[str] | t.TextIO) -> str:
    if isinstance(file, (str, os.PathLike)):  # Type(s) which can be tested at runtime
        with open(file) as fileIO:
            return read_file(fileIO)
    else:  # Type(s) which can't be tested at runtime
        return file.read()

如果您的类型检查器支持使用

hasattr
缩小类型,这也可能是一个选项(mypy-play.net):

import os
import typing as t

def read_file(file: str | os.PathLike[str] | t.TextIO) -> str:
    if hasattr(file, "read"):  # Not `str` or `os.PathLike`, as they don't have the `read` attribute
        return file.read()
    else:
        with open(file) as fileIO:
            return read_file(fileIO)
© www.soinside.com 2019 - 2024. All rights reserved.