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

60

在现代Python版本中,可以使用函数注释进行静态类型分析,参考PEP 484。通过typing模块,这变得非常容易。

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

def myfunction(file: FILETYPE):
    pass

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

我应该在FILETYPE中插入什么?

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

是否有一种通用的“文件”类型?


注释必须引用磁盘上的物理文件,还是可以包括像StringIO这样的类文件对象? - jwodder
相关链接:https://dev59.com/uVkT5IYBdhLWcg3wc_NR,https://dev59.com/IWAf5IYBdhLWcg3wlDcD - djvg
4个回答

60

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

typing.IO
typing.TextIO
typing.BinaryIO

通用类型IO[AnyStr]及其子类TextIO(IO[str])BinaryIO(IO[bytes])代表诸如open()返回的I/O流类型。


1
自版本3.8开始,typing.IOtyping.TextIOtyping.BinaryIO 已被弃用,并将在版本3.12中移除,详情请参阅typing文档 - lead-free
9
我相信弃用声明只适用于 typing.io 命名空间。请参阅 此链接此链接 - Eugene Yarmash
尽管目前似乎没有其他替代方案可以让mypy认可(io.IOBase及其子类),但这并不意味着没有其他选择。 - lead-free
2
@EugeneYarmash是正确的。这里是来自文档的实际弃用消息:“typing.io 命名空间已被弃用并将被删除。这些类型应该直接从 typing 导入。 - djvg

18

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

请注意,这也包括像 io.StringIOio.BytesIO 这样的内存流。有关详细信息,请阅读 模块io 的文档。


1
仅作评论:虽然这可能是我能得到的“最佳”答案。但问题仍未得到解决。很多事情都依赖于_io._base和从中派生出来的东西。但是对于_io._base和io.base,没有“通用”的顶层层? - paul23
@paul23 我不明白你的意思。据我所知,io.IOBase 是指向“字节流”的最佳类型提示,并且可以使用标准库创建的每个类似文件的对象都是它的实例。如果 IOBase 不符合您对字节流的想法,或者您有一个不适合它作为类型提示的用例,您可能需要编辑您的问题并解释原因。 - Stop harming Monica
如果您使用BytesIO打开一个内存字节流,则它派生自_BufferedIOBase,后者又派生自_IOBase。 - paul23
@paul23 这有什么问题吗?以防你没有注意到,io.BytesIO 也是继承自 io.IOBase - Stop harming Monica

6

要么是这样:

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)它可以避免循环导入

2
鉴于类型提示 PEP 484 中的一个目标,我不明白为什么避免在类型提示中使用导入是一个好的实践。 - gerardw

1

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不应该被使用。这个模块难道不是只为类型检查器工具的开发人员而设计的吗? - pabouk - Ukraine stay strong
在这种情况下,我们不是为自己开发了一个自定义类型检查工具的程序员吗?Python类型检查是可选的,因此使用'if TYPE_CHECKING'。 - Kache

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接