具有长度的对象的类型

时间:2018-04-06 15:30:42

标签: python types

如何键入提示参数或isinstance检查必须可迭代的对象并与len一起使用?我假设几乎所有具有长度的对象都是可迭代的,所以这实际上是关于什么类型(如果有的话)表示实现__len__的对象。

def n_and_list(x: ???):
    return len(x), [y for y in x]

不是typing.Iterablecollections.Iterable,因为对于没有长度的内容,例如zip,这些都是正确的。

In [1]: from typing import Iterable

In [2]: isinstance(zip([]), Iterable)
Out[3]: True

In [3]: from collections import Iterable

In [4]: isinstance(zip([]), Iterable)
Out[4]: True

In [5]: len(zip([]))
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-27-86d411a5426c> in <module>()
----> 1 len(zip([]))

TypeError: object of type 'zip' has no len()

它不是typing.Sequencecollections.Sequence,因为对于有长度的东西,如字典键和numpy数组,这些都是错误的。

In [6]: from typing import Sequence

In [7]: isinstance({}.keys(), Sequence)
Out[7]: False

In [8]: from numpy import asarray

In [9]: isinstance(asarray([]), Sequence)
Out[9]: False

In [10]: from collections import Sequence

In [11]: isinstance({}.keys(), Sequence)
Out[11]: False

In [12]: from numpy import asarray

In [13]: isinstance(asarray([]), Sequence)
Out[13]: False

它不是iterableiter,因为它们不是类型。它不是listtuple,因为它们太窄了。

2 个答案:

答案 0 :(得分:2)

对此有collections.Sized

def n_and_list(x: collections.Sized):
    return len(x), [y for y in x]

或者对于您的情况,因为您希望它具有__len__并且可以迭代:

import typing
import collections

def n_and_list(x: typing.Union[collections.Sized, collections.Iterable]):
    return len(x), [y for y in x]

可悲的是,正如下面提到的评论所述,目前还没有typing.Intersection来保证两者的存在。

答案 1 :(得分:2)

如果您不包含typing.Collection,则可以使用__contains__

class Collection(Sized, Iterable, Container):

    __slots__ = ()

    @classmethod
    def __subclasshook__(cls, C):
        if cls is Collection:
            return _check_methods(C,  "__len__", "__iter__", "__contains__")
        return NotImplemented

如果你这样做,只需删除对SizedIterable

的检查,__contains__就可以轻松实现。
class SizedIterable(Sized, Iterable):

    __slots__ = ()

    @classmethod
    def __subclasshook__(cls, C):
        if cls is SizedIterable:
            return _check_methods(C,  "__len__", "__iter__")
        return NotImplemented