【问题标题】:Python Iterable vs SequencePython 可迭代与序列
【发布时间】:2022-11-18 00:39:40
【问题描述】:

我不明白提示IterableSequence 时的区别。

这两者之间的主要区别是什么以及何时使用哪个?

我认为setIterable而不是Sequence,是否有任何内置数据类型是Sequence而不是Iterable

def foo(baz: Sequence[float]):
  ...

# What is the difference?
def bar(baz: Iterable[float]):
  ...

【问题讨论】:

    标签: python-3.x type-hinting


    【解决方案1】:

    SequenceIterable 抽象基类(也可以用作类型注释)遵循 Python 对 sequenceiterable 的定义。再具体一点:

    • 可迭代的是定义__iter__的任何对象或者__getitem__
    • 序列是定义__getitem____len__ 的任何对象。根据定义,任何序列都是可迭代的。 Sequence 类还定义了其他方法,例如调用这两个必需方法的__contains____reversed__

    一些例子:

    • listtuplestr 是最常见的序列。
    • 一些内置的可迭代对象不是序列。例如,reversed 返回无法下标的 reversed 对象(或 list_reverseiterator 用于列表)。

    【讨论】:

    • “一些内置迭代器不是序列”- 我认为全部内置迭代器不是序列。或者你知道一个吗?
    • @joel 是的,这是可能的,尽管他们确实以迭代器为例。如果他们打算谈论可迭代对象,我会说字典或集合将是更突出的例子。 (现在我正在重新阅读他们的答案,我发现他们对“序列”的释义定义实际上是错误的。也许这就是为什么他们没有使用字典作为例子。)
    • @KellyBundy 一个例子是rangeisinstance(range(5), collections.abc.Sequence) 返回真。 set 不是序列,因为它没有定义 __getitem__dict 是一个有趣的例子,因为它确实同时定义了__getitem____len__,但是docs 明确提到它不是一个序列,因为它的__getitem__ 接受任意类型而不仅仅是int
    • range(5) 不是迭代器。 iter(range(5)) 是(它是一个range_iterator),而isinstance(iter(range(5)), collections.abc.Sequence) 按预期返回False
    • 啊,明白了,谢谢指正。我应该改变我的答案说可迭代的代替迭代器.
    【解决方案2】:

    在编写带有 items 参数的函数/方法时,我通常更喜欢 Iterable 而不是 Sequence。 以下是原因,我希望它能帮助理解差异。

    my_func_1是:

    from typing import Iterable
    def my_func_1(items: Iterable[int]) -> None:
        for item in items:
            ...
            if condition:
                break
        return
    

    Iterable 为来电者提供最大的可能性。正确的调用包括:

    my_func_1((1, 2, 3)) # tuple is Sequence, Collection, Iterator
    my_func_1([1, 2, 3]) # list is MutableSequence, Sequence, Collection, Iterator
    my_func_1({1, 2, 3}) # set is Collection, Iterator
    my_func_1(my_dict) # dict is Mapping, Collection, Iterator
    my_func_1(my_dict.keys()) # dict.keys() is MappingKeys, Set, Collection, Iterator
    my_func_1(range(10)) # range is Sequence, Collection, Iterator
    my_func_1(x**2 for x in range(100)) # "strict' Iterator, i.e. neither a Collection nor a Sequence
    ... 
    

    ...因为都是Iterable

    给函数调用者的隐含信息是:“按原样”传输数据,只是不要转换它。

    如果调用者没有数据作为Sequence(例如tuplelist)或作为非SequenceCollection(例如set),并且因为迭代在@987654335 之前中断@,如果他提供一个“严格的”Iterator,它的表现也会更好。

    但是,如果函数算法(例如my_func_2)需要多次迭代,那么如果调用者提供“严格”IteratorIterable 将失败,因为第一次迭代会耗尽它。因此使用Collection

    from typing import Collection
    def my_func_2(items: Collection[int]) -> None:
        for item in items:
            ...
        for item in items:
            ...
        return
    

    如果函数算法 (my_func_3) 必须通过索引访问特定项目,那么如果调用者提供集合、Mapping 或“严格”Iterator,则IterableCollection 都将失败。 因此使用Sequence

    from typing import Sequence
    def my_func_3(items: Sequence[int]) -> None:
        return items[5]
    

    结论:策略是:“使用函数可以处理的最通用的类​​型”。不要忘记所有这些只是关于打字,以帮助静态类型检查器报告不正确的调用(例如,在需要 Sequence 时使用 set)。然后调用者有责任在必要时转换数据,例如:

    my_func_3(tuple(x**2 for x in range(100)))
    

    实际上,所有这些都与缩放项目长度时的性能有关。 如果可能,总是首选Iterator。性能应作为日常任务处理,而不是作为消防员任务组处理。

    在那个方向上,您可能会遇到这样的情况:一个函数只处理空用例并委托其他用例,并且您不想将项目转换为 CollectionSequence。然后做这样的事情:

    from more_itertools import spy
    def my_func_4(items: Iterable[int]) -> None:
        (first, items) = spy(items)
        if not first: # i.e. items is empty
            ...
        else:
            my_func_1(items) # Here 'items' is always a 'strict' Iterator
        return
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-02-09
      • 2014-04-26
      • 2015-07-23
      • 2019-01-15
      • 1970-01-01
      • 2019-11-29
      • 1970-01-01
      • 2016-05-29
      相关资源
      最近更新 更多