【问题标题】:Typehints for Sized Iterable in PythonPython 中 Sized Iterable 的类型提示
【发布时间】:2018-08-31 20:22:02
【问题描述】:

我有一个函数在它的一个参数上使用len 函数并迭代参数。现在我可以选择是否使用IterableSized 来注释类型,但两者都会在mypy 中给出错误。

from typing import Sized, Iterable


def foo(some_thing: Iterable):
    print(len(some_thing))
    for part in some_thing:
        print(part)

给予

error: Argument 1 to "len" has incompatible type "Iterable[Any]"; expected "Sized"

虽然

def foo(some_thing: Sized):
...

给予

error: Iterable expected
error: "Sized" has no attribute "__iter__"

由于没有Intersection 中讨论的this issue,因此我需要某种混合类。

from abc import ABCMeta
from typing import Sized, Iterable


class SizedIterable(Sized, Iterable[str], metaclass=ABCMeta):
    pass


def foo(some_thing: SizedIterable):
    print(len(some_thing))
    for part in some_thing:
        print(part)


foo(['a', 'b', 'c'])

foolist 一起使用时会出错。

error: Argument 1 to "foo" has incompatible type "List[str]"; expected "SizedIterable"

这并不奇怪,因为:

>>> SizedIterable.__subclasscheck__(list)
False

所以我定义了一个__subclasshook__(参见docs)。

class SizedIterable(Sized, Iterable[str], metaclass=ABCMeta):

    @classmethod
    def __subclasshook__(cls, subclass):
        return Sized.__subclasscheck__(subclass) and Iterable.__subclasscheck__(subclass)

然后子类检查工作:

>>> SizedIterable.__subclasscheck__(list)
True

但是mypy 仍然抱怨我的list

error: Argument 1 to "foo" has incompatible type "List[str]"; expected "SizedIterable"

在使用len 函数和迭代我的参数时,如何使用类型提示?我认为投射foo(cast(SizedIterable, ['a', 'b', 'c'])) 不是一个好的解决方案。

【问题讨论】:

    标签: python python-3.x type-hinting


    【解决方案1】:

    将来会引入Protocols。它们已通过typing_extensions 提供。另见PEP 544。使用Protocol 上面的代码将是:

    from typing_extensions import Protocol
    
    
    class SizedIterable(Protocol):
    
        def __len__(self):
            pass
    
        def __iter__(self):
            pass
    
    
    def foo(some_thing: SizedIterable):
        print(len(some_thing))
        for part in some_thing:
            print(part)
    
    
    foo(['a', 'b', 'c'])
    

    mypy 毫无怨言地接受该代码。但是 PyCharm 说的是

    预期类型 'SizedIterable',得到 'List[str]'

    关于最后一行。

    【讨论】:

    【解决方案2】:

    从 Python3.6 开始,有一个名为 Collection 的新类型。见here

    【讨论】:

    • 当使用len()函数时,Collection是否优于Iterator
    • Iterator 没有大小。见这里:docs.python.org/3/library/…
    • PyCharm 在 ItemView (dict.items()) 时似乎仍然会发出警告
    • Value of type "Collection[Any]" is not indexable... 有没有可以迭代的类型,可以调用 len() 并按索引获取项目?!
    • @JamesOwers:这是一个序列。
    【解决方案3】:

    如果您打算仅使用列表或元组并按索引访问其元素,例如x[0],则应使用typing 中的SequenceSequence 既是 Sized 又是 Iterable,参见 here

    【讨论】:

    • 这不适用于常规集和冻结集。他们没有按照序列的预期实现__getitem__
    • 好点。它仅适用于可索引(实现__getitem__),如列表或元组。在更一般的情况下,按照 Avision 的建议,使用 Collection
    • 序列是Iterable,因为需要__getitem__ 方法,而不仅仅是Iterable
    【解决方案4】:

    也许你需要这样的课程?

    class MyArray:
      _array: list
    
      def as_tuple(self):
        return tuple(self._array)
    
      def as_list(self):
        return self._array
        
        # More Feature to implement
    
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-07-03
      • 2021-10-22
      • 2020-01-14
      • 2022-09-29
      • 2016-05-15
      • 1970-01-01
      • 2021-08-16
      • 2017-01-26
      相关资源
      最近更新 更多