11

我正在运行的代码是:

>>> from collections import abc
>>> mydict = {'test_key': 'test_value'}
>>> isinstance(mydict, abc.Mapping)
True

我明白是什么isinstance,但我不确定abc.Mappingfrom是什么collections

好像这条线isinstance(mydict, abc.Mapping)被用来检查那mydict是一本字典?

这样做不是更容易 isinstance(mydict, dict)吗?

我进行了一些搜索并在此线程中找到了相关评论:检查 Python 变量类型的最佳(惯用)方法是什么?,但我仍然无法弄清楚为什么abc.Mapping在这里使用比仅使用更可取dict

4

3 回答 3

21

collections.abc为容器提供了一系列抽象基类

该模块提供了抽象基类,可以用来测试一个类是否提供了特定的接口;例如,它是可散列的还是映射的。

它们允许您检查某个对象是否具有类似于您正在检查的 ABC 的行为,而无需关心实际的实现。

例如,假设你有一个函数 F 根据参数的类型做某事,你可以直接检查是否是 list 或 tuple 或 dict 等的实例,然后做你的工作,但这限制你只有要使用这些,如果您然后创建自己的类具有类似的行为来说出列表,在某些情况下您关心并想将它与 F 一起使用,您发现它不起作用,那么您必须修改 F接受您的课程,但如果您检查 ABC,则不需要进行此类修改

现在是一个工作示例:假设您想要一个函数,该函数可以将列表中的所有元素都放在相同的位置,那么您可以这样做

def even_pos(data):
    if isinstance(data,list):
        return [data[i] for i in range(0,len(data),2)]
    else:
        raise ValueError("only a list")

并用作

>>> test = list(range(20))
>>> even_pos(test)
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
>>>

没问题,但是你意识到元组与 this 函数关注的列表是一样的,你也可以将该检查添加到函数中,一切都很好,但是你的朋友告诉你他想使用你的功能,但他正在使用 acollections.deque然后你的另一个朋友告诉...看到这里的模式了吗?我提到的所有对象(列表、元组、双端队列)都有相同的东西,并且可以通过该示例函数以相同的方式使用,并且所有这些行为都是在 ABC 中压缩的,所以isinstance(data,(list,tuple,collections.deque,...)你只需要isinstance(data,abc.Sequence)和该功能看起来像

from collections import abc
def even_pos(data):
    if isinstance(data,abc.Sequence):
        return [data[i] for i in range(0,len(data),2)]
    else:
        raise ValueError("only a Sequence")

>>> even_pos( list(range(10)) )
[0, 2, 4, 6, 8]
>>> even_pos( tuple(range(10)) )
[0, 2, 4, 6, 8]
>>> even_pos( range(10) )  # in python 3 range don't return a list, but a range object
[0, 2, 4, 6, 8]
>>> even_pos( "asdfghjh" )
['a', 'd', 'g', 'j']
>>> 

现在您不需要知道正在使用的实际实现,只需知道它具有您想要的行为

于 2016-02-29T02:49:26.950 回答
7

collections.abc模块提供了几个抽象基类,可用于一般性地描述 Python 中的各种数据结构。在您的示例中,您测试一个对象是否是Mapping抽象类的实例,这对于许多“像字典一样工作”的类都是正确的(例如,它们有一个__getitem__采用可散列键和返回值的方法, havekeysvalues方法items, ETC。)。这样dict的对象可能继承自,dict但它们不需要。

中的抽象类型collections.abc是使用顶级abc模块实现的。dictregister编辑为 a MutableMapping(它是 的子类Mapping),因此isinstance检查将接受字典作为 aMapping即使Mapping不是dict.

于 2016-02-29T02:29:35.487 回答
4

collections.abc.Mapping是首选,因为它为此类容器定义了抽象 api,因为dict它只是此类容器的实现。有点过于简单化了,但这是关键——dict不是接口/抽象/api/...

不是 dict 实例的对象的示例是MultiDict,广泛用于 Web 框架(例如 aiohttp)。

于 2016-02-29T02:20:17.507 回答