如何编写一个满足typing.TextIO的类似文件的类?

mkr*_*er1 11 python type-hinting mypy python-typing

io例如,在编写实现类文件接口的类时,我们可以从模块继承抽象基类之一,如调整迭代器以使其行为类似于 Python 中的类文件对象TextIOBase中所示。

另一方面,在类型注释中,我们应该使用派生自typing.IO(例如TextIO)的类来表示此类对象,如文件或类文件对象的类型提示中所示?Union 中 io.TextIOBase 的类型检查问题

然而,这似乎并没有像我预期的那样工作:

import io
import sys
import typing

class MyIO(io.TextIOBase):
    def write(self, text: str):
        pass

def hello(f: typing.TextIO):
    f.write('hello')

hello(sys.stdout)             # type checks
hello(open('temp.txt', 'w'))  # type checks
hello(MyIO())                 # does not type check
Run Code Online (Sandbox Code Playgroud)

当在此代码上运行 mypy 时(使用 Python 3.7.3 和 mypy 0.910),我们得到

错误:“hello”的参数 1 具有不兼容的类型“MyIO”;预期“TextIO”

问题

如何MyIO编写该类,使其被接受为类型的函数参数typing.TextIO(而不仅仅是使用typing.cast(typing.TextIO, ...))?

失败的尝试

  1. typing.TextIO不能用作基类:

    使用时class MyIO(typing.TextIO)

    错误:无法使用抽象属性“__enter__”、“__exit__”、...和“writelines”实例化抽象类“MyIO”(抑制了 15 种方法)

    使用时class MyIO(io.TextIOBase, typing.TextIO):

    错误:基类“IOBase”中“readlines”的定义与基类“IO”中的定义不兼容

    对于其他几种方法也是如此。

  2. 重写__new__和注释typing.TextIO为返回类型不起作用:

    def __new__(cls, *args, **kwargs) -> typing.TextIO:                        
        return super().__new__(cls, *args, **kwargs)
    
    Run Code Online (Sandbox Code Playgroud)

    结果是

    错误:“__new__”的返回类型不兼容(返回“TextIO”,但必须返回“MyIO”的子类型)
    错误:返回值类型不兼容(得到“MyIO”,预期为“TextIO”)

或者这已经应该可以工作了,而我使用的 Python 和/或 mypy 版本太旧了?但是,使用--python-version 3.8or3.93.10作为 mypy 的选项不会改变任何内容。

iti*_*oej 3

改用io.StringIO

import io
import sys
import typing


class MyIO(io.StringIO):
    def write(self, text: str):
        pass


def hello(f: typing.TextIO):
    f.write("hello")


hello(sys.stdout)             # type checks
hello(open("temp.txt", "w"))  # type checks
hello(MyIO())                 # type checks
 
Run Code Online (Sandbox Code Playgroud)

  • [`io.StringIO`](https://docs.python.org/3/library/io.html#io.StringIO) 是内存中文本文件的具体实现;我认为对于任意类似文件的实现的基本类型来说,这根本不是一个好的选择。 (3认同)