关于python:isInstance和dictionary和abc.mapping从集合collections中做什么?

What does isinstance with a dictionary and abc.Mapping from collections doing?

我运行的代码是:

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

我知道isinstance是做什么的,但我不知道abc.Mapping是做什么的?

似乎用isinstance(mydict, abc.Mapping)行来检查mydict是否是字典?

这不是更容易吗EDOCX1?5?

我做了一些搜索,在这个线程中找到了相关的注释:检查python变量类型的最佳(惯用)方法是什么?但是我还是很难弄清楚为什么在这里使用abc.Mapping比仅仅使用dict更好。


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

This module provides abstract base classes that can be used to test whether a class provides a particular interface; for example, whether it is hashable or whether it is a mapping.

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

例如,假设您有一个函数f,它根据参数的类型来做一些事情,您可以直接检查它是否是list或tuple或dict或etc的实例,并完成您的工作,但是这限制了您只需要使用这些实例,如果您随后使自己的类具有类似的行为来说出list,在某些情况下,您会关心,并且wan不要将它与f一起使用,你会发现它不起作用,然后你必须修改f来接受你的类,但是如果你检查abc,这种修改是不必要的。

现在有一个工作示例:假设您想要一个函数,它从一个列表中给出所有元素的偶数位置,那么您可以这样做。

1
2
3
4
5
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")

并用作

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

没问题,但是当你意识到一个元组和这个函数中的一个列表是一样的,你也可以把这个检查添加到函数中,一切都很好,但是你的朋友告诉你他想使用你的函数,但是他使用的是collections.deque,然后你的另一个朋友告诉你……看到图案了吗?我提到的所有对象(list、tuple、deque)都有相同的东西,并且可以用同样的方式被示例函数使用,所有这些行为都是在abc中压缩的,所以您只需要isinstance(data,abc.Sequence),而不是isinstance(data,(list,tuple,collections.deque,...),函数看起来像

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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']
>>>

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


collections.abc模块提供了几个抽象的基类,可以用来一般地描述Python中的各种数据结构。在您的示例中,测试对象是否是Mapping抽象类的实例,对于许多"像字典一样工作"的类来说,这是正确的(例如,它们有一个__getitem__方法,该方法采用哈希键和返回值,有keysvaluesitems方法等)。这种类似于dict的物体可能是从dict继承的,但它们不需要。

collections.abc中的抽象类型是使用顶级abc模块实现的。dictregistered作为MutableMapping的一个子类(是Mapping的一个子类),因此,即使Mapping不是dict的实际基类,isinstance检查也将接受字典作为Mapping的字典。


由于dict只是此类容器的一个实现,因此最好使用collections.abc.Mapping,因为它为此类容器定义了抽象API。有点过于简单,但这是键-dict不是接口/abstract/api/。

对象不是dict实例的例子是多方面的,广泛应用于Web框架(例如aiohtp)。