在 Python 中注释“文件类型”的正确方法

Posted

技术标签:

【中文标题】在 Python 中注释“文件类型”的正确方法【英文标题】:The correct way to annotate a "file type" in Python 【发布时间】:2017-06-04 13:42:57 【问题描述】:

根据PEP 484,在现代版本的 Python 中,可以使用函数注释进行静态类型分析。这可以通过打字模块轻松完成。

现在我想知道如何为“文件流”提供“类型提示”。

def myfunction(file: FILETYPE):
    pass

with open(fname) as file:
    myfunction(file)

我将插入什么作为FILETYPE

使用print(type(file)) 会返回<class '_io.TextIOWrapper'>,这根本不清楚。

没有通用的“文件”类型吗?

【问题讨论】:

注解是否必须引用磁盘上的物理文件,或者它还可以包含类似文件的对象,如StringIO 相关:***.com/q/38569401,***.com/q/24501462 【参考方案1】:

您可以使用typing.IOtyping.TextIOtyping.BinaryIO 来表示不同类型的 I/O 流。引用documentation:

类打字.IO 类打字.TextIO 类打字.BinaryIO

泛型类型IO[AnyStr]及其子类TextIO(IO[str])BinaryIO(IO[bytes])代表I/O流的类型,例如 由open()返回。

【讨论】:

typing.IOtyping.TextIOtyping.BinaryIO 自 3.8 版以来已弃用,每次键入 documentation 时将在 3.12 版中删除。 @lead-free 我相信弃用说明仅适用于 typing.io 命名空间。见this 和this。 虽然目前似乎没有 mypy 会尊重的typing.io 的替代品(io.IOBase 和它的孩子)。 @EugeneYarmash 是对的。以下是来自docs 的实际弃用消息:“typing.io 命名空间 已弃用并将被删除。这些类型应直接从 typing 而是。"【参考方案2】:

我想你想要io.IOBase,“[t]他是所有 I/O 类的抽象基类,作用于字节流。”

请注意,这也包括内存中的流,例如 io.StringIOio.BytesIO。请阅读module io 上的文档了解详细信息。

【讨论】:

就像评论一样:虽然这是我可能得到的“最佳”答案。这个问题仍然没有解决。很多事情都依赖于 _io._base 及其衍生产品。 _io._baseio.base 都没有“通用”顶层? @paul23 我不明白你的意思。 AFAIK io.IOBase 是对“字节流”的最佳类型提示,并且可以使用标准库创建的每个类似文件的对象都是它的一个实例。如果IOBase 与您对字节流的想法不符,或者您的用例不是一个好的类型提示,您可能需要编辑您的问题并解释原因。 例如,如果您使用 BytesIO 打开内存中的字节流;这源自_BufferedIOBase,它源自_IOBase @paul23 这是怎么回事?万一你没有注意到,io.BytesIO 也继承自 io.IOBase【参考方案3】:

要么:

from typing import TextIO # or IO or BinaryIO

def myfunction(file: TextIO ):
    pass

这个

from typing import TYPE_CHECKING
if TYPE_CHECKING:
    from typing import TextIO # or IO or BinaryIO

def myfunction(file: 'TextIO'):
    pass

第二种方法将避免在执行期间导入类。尽管 python 在执行期间仍然必须导入 TYPE_CHECKING,但最好避免导入仅用于类型提示的类:(1) 不执行(只是解析),以及 (2) 它可以避免循环导入

【讨论】:

鉴于类型提示 PEP 484 的目标之一,我不明白为什么避免导入类型提示是一种好习惯。【参考方案4】:

typeshed 有一个SupportsRead 协议:

from __future__ import annotations
from typing import TYPE_CHECKING, AnyStr

if TYPE_CHECKING:
    from _typeshed import SupportsRead


def takes_readable_str(fo: SupportsRead[str]):
    return fo.read()

def takes_readable_bytes(fo: SupportsRead[bytes]):
    return fo.read()

def takes_readable_any(fo: SupportsRead[AnyStr]):
    return fo.read()

【讨论】:

我会说不应该使用私有模块_typeshed。该模块不是仅适用于类型检查器工具的开发人员吗? 在这种情况下,我们不是有效地为自己开发自定义类型检查器工具吗? Python 类型检查是可选的,因此 if TYPE_CHECKING.

以上是关于在 Python 中注释“文件类型”的正确方法的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Python 中使用 ruamel.yaml 从 YAML 文件中获取注释?

绘图中的音频文件长度不正确,以及python中audioplot上的注释段不正确覆盖

ftl格式文件的注释方法?

是否可以在Python中创建多行注释?

VSCode 中 js 文件类型注释报错的问题解决

记录文件、类和构造函数的正确方法是啥?