【问题标题】:How to type-annotate object returned by csv.writer?如何对 csv.writer 返回的对象进行类型注释?
【发布时间】:2023-04-11 10:57:02
【问题描述】:

我想将类型注释应用于csv.writer 的返回对象,以符合更大的代码库。不幸的是,我无法确定合适的返回类型。

>>> import csv
>>> writer = csv.writer(open('outfile.csv', 'w'))
>>> type(writer)
<class '_csv.writer'>

如果我尝试使用这个类名:

>>> import _csv
>>> writer: _csv.writer = csv.writer(open('outfile.csv', 'w'))

我收到以下 mypy 错误:

Invalid type "_csv.writer"

有人知道在这种情况下使用哪种类型吗?当然,我可以使用typing.Any,但这会使类型注释的意义无效。

【问题讨论】:

    标签: python mypy


    【解决方案1】:

    简短的回答是没有办法直接访问该类型。阅读C source of the _csv module会发现readerwriter的类型没有暴露。即使在 _csv 模块在 Python 中实现的Pypy 中,也不会公开类型。

    因此,如果您需要使用它,您需要使用一种变通方法,通过实例化编写器的临时实例并获取它的类型。

    import csv
    # We'll need a temporary file-like object, so use a tempfile
    from tempfile import TemporaryFile
    
    with TemporaryFile() as t:
        CSVReader = type(csv.reader(t))
        CSVWriter = type(csv.writer(t))
    
    w: CSVWriter = csv.writer('path/to/data.csv')
    

    如果您想将此逻辑分开,我建议在单独的模块中创建类型

    from csv_types import CSVReader, CSVWriter
    

    另一种解决方案(也包括编写您自己的类型模块),是在它为iore 的类型定义中遵循typing 模块的示例。

    【讨论】:

    • 该变通方法在 Python 控制台上运行良好。但是它不适用于像 mypy 这样的静态类型检查器。将出现InvalidType 错误。我认为这是因为 mypy 实际上并没有执行代码。但是,感谢您对第二种解决方案的提示。这实际上让我走上了正轨。
    【解决方案2】:

    一种解决方案是编写一个表示类型的抽象类。这也是typing 模块中某些类的处理方式。对于csv.writer() 函数如下:

    class _CSVWriter:
    
        @abstractmethod
        def writerow(self, row: List[str]) -> None:
            pass
    
        @abstractmethod
        def writerows(self, rows: List[List[str]]) -> None:
            pass
    
        @abstractproperty
        def dialect(self) -> csv.Dialect:
            pass
    

    现在可以在writer 对象的类型注释中使用此类。由于返回对象的类型名仍然是_csv.writer,您仍然会收到类型错误。为避免这种情况,您需要将其转换为 _CSVWriter 对象。

    from typing import cast
    writer: _CSVWriter = cast(_CSVWriter, csv.writer(open('test', 'w'))
    

    该解决方案有点冗长,但可以完成工作。

    【讨论】:

      【解决方案3】:

      通常当事情表现得很奇怪时,这表明 typeshed 并不完全映射到运行时。如果您查看 _csv in typeshed,您将看到该类型名为 _writer。所以你应该可以注释到_csv._writer

      【讨论】:

      • 这导致error: Name 'csv._writer' is not defined
      • 你需要使用_csv._writer
      • 确实,typeshed 已更改,因此 _writer 不再重新导出。我更新了答案。
      • 感谢这个答案,我了解了 Typeshed,它“包含 Python 标准库和 Python 内置函数的外部类型注释,以及这些项目外部人员贡献的第三方包。”此外,“如果使用 mypy,您根本不需要与 typeshed 存储库进行交互:typeshed 的标准库部分的副本与 mypy 捆绑在一起。”
      • @DanielChin 使其成为字符串文字 import _csv 然后 f: '_csv._writer'
      【解决方案4】:

      我遇到了 typeshed defs 的问题,最终使用了以下内容:

      class Writer(Protocol):
          def writerow(self, row: Iterable[Any]) -> Any:
              ...
      
          def writerows(self, rows: Iterable[Iterable[Any]]) -> None:
              ...
      
      
      Reader = Iterator[Any]
      
      writer: Writer = csv.writer(open('outfile.csv', 'w'))
      reader: Reader = csv.writer(open('outfile.csv', 'w'))
      

      【讨论】:

        猜你喜欢
        • 2021-06-06
        • 2021-11-09
        • 2020-10-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-02-26
        • 1970-01-01
        • 2019-05-14
        相关资源
        最近更新 更多