我正在运行的代码是:
>>> from collections import abc
>>> mydict = {'test_key': 'test_value'}
>>> isinstance(mydict, abc.Mapping)
True
我明白
isinstance
的作用,但我不确定 abc.Mapping
的 collections
的作用是什么?
看起来
isinstance(mydict, abc.Mapping)
行被用来检查mydict
是否是一本字典?
这样不是更容易做到吗
isinstance(mydict, dict)
?
我做了一些搜索并在此线程中找到了相关评论:What is the best (idiomatic) way to check the type of a Python variable?,但我仍然无法弄清楚为什么在这里使用
abc.Mapping
更可取与仅使用 dict
相比。
collections.abc为容器提供了一系列抽象基类。
该模块提供了抽象基类,可以用来测试一个类是否提供了特定的接口;例如,它是否可散列或是否是映射。
它们允许您检查某个对象是否具有与您正在检查的 ABC 类似的行为,而无需关心实际实现。
例如,假设您有一个执行某些操作的函数 F:根据参数的类型,您可以直接检查 是否是 列表或元组或字典等的实例,并完成您的工作,但这限制了您只能使用这些类型的集合。 如果您然后创建自己的类,该类具有类似的行为来表示列表(在某些情况下您关心)并希望将其与 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]
>>>
没问题,但随后您意识到元组与此函数所关注的列表相同,因此您也可以将该检查添加到函数中。
然后你的朋友告诉你他想使用你的函数,但他正在使用
collections.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']
>>>
现在您不需要知道正在使用的实际实现,只需知道它具有您想要的行为即可。
collections.abc
模块提供了几个抽象基类,可用于通用描述Python中的各种数据结构。在您的示例中,您测试一个对象是否是 Mapping
抽象类的实例,这对于许多“像字典一样工作”的类来说都是如此(例如,它们有一个 __getitem__
方法,该方法采用可哈希键和返回值,有 keys
、values
和 items
方法等)。这种类似 dict
的对象可能会继承自 dict
,但它们不需要。
collections.abc
中的抽象类型是使用顶级abc
模块实现的。 dict
被 register
编辑为 MutableMapping
(它是 Mapping
的子类),因此 isinstance
检查将接受字典作为 Mapping
,即使 Mapping
不是实际的基础dict
的课程。
collections.abc.Mapping
是首选,因为它为这种类型的容器定义了抽象API,因为dict
只是此类容器的实现。有点过于简单化了,但这就是关键 - dict
不是接口/抽象/api/...
非 dict 实例的对象示例是 MultiDict,广泛用于 Web 框架(例如 aiohttp)。