如何编写满足typing.TextIO要求的类似文件的类?

2024-05-08 22:20:01 发布

您现在位置:Python中文网/ 问答频道 /正文

在编写实现类文件接口的类时,我们可以从io模块继承一个抽象基类,例如TextIOBase,如Adapt an iterator to behave like a file-like object in Python所示

另一方面,在类型注释中,我们应该使用从typing.IO(例如TextIO)派生的类来表示这样的对象,如Type hint for a file or file-like object?Type-checking issue with io.TextIOBase in a Union所示

然而,这似乎并不像我预期的那样有效:

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

在这段代码上运行mypy时(使用python3.7.3和mypy0.910),我们得到

error: Argument 1 to "hello" has incompatible type "MyIO"; expected "TextIO"

问题

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

尝试失败

  1. 使用typing.TextIO作为基类是不可能的:

    使用class MyIO(typing.TextIO)时:

    error: Cannot instantiate abstract class "MyIO" with abstract attributes "__enter__", "__exit__", ... and "writelines" (15 methods suppressed)

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

    error: Definition of "readlines" in base class "IOBase" is incompatible with definition in base class "IO"

    其他几种方法也是如此

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

    def __new__(cls, *args, **kwargs) -> typing.TextIO:                        
        return super().__new__(cls, *args, **kwargs)
    

    导致

    error: Incompatible return type for "__new__" (returns "TextIO", but must return a subtype of "MyIO")
    error: Incompatible return value type (got "MyIO", expected "TextIO")

或者这应该已经起作用了,而我使用的Python和/或mypy版本太旧了?但是,使用--python-version 3.83.93.10作为mypy的选项不会改变任何事情


Tags: iniotyping类型hellonewreturntype
2条回答

改用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
 

我认为您的第一次尝试实际上是正确的,它只需要您实现所有抽象方法(如错误所示)。你不必把实际的逻辑放在那里。下面的类将完成此任务:

import io
from types import TracebackType
from typing import Optional, Type, Iterator, AnyStr, Iterable, TextIO

import sys
import typing

class MyIO(typing.TextIO):
    def __enter__(self) -> TextIO:
        pass

    def close(self) -> None:
        pass

    def fileno(self) -> int:
        pass

    def flush(self) -> None:
        pass

    def isatty(self) -> bool:
        pass

    def read(self, n: int = ...) -> AnyStr:
        pass

    def readable(self) -> bool:
        pass

    def readline(self, limit: int = ...) -> AnyStr:
        pass

    def readlines(self, hint: int = ...) -> typing.List[AnyStr]:
        pass

    def seek(self, offset: int, whence: int = ...) -> int:
        pass

    def seekable(self) -> bool:
        pass

    def tell(self) -> int:
        pass

    def truncate(self, size: Optional[int] = ...) -> int:
        pass

    def writable(self) -> bool:
        pass

    def writelines(self, lines: Iterable[AnyStr]) -> None:
        pass

    def __next__(self) -> AnyStr:
        pass

    def __iter__(self) -> Iterator[AnyStr]:
        pass

    def __exit__(self, t: Optional[Type[BaseException]], value: Optional[BaseException],
                 traceback: Optional[TracebackType]) -> Optional[bool]:
        pass

    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

相关问题 更多 >