16

The code I'm running is:

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

I understand what isinstance does, but I'm not sure what abc.Mapping does from collections?

It seems like the line isinstance(mydict, abc.Mapping) is being used to check that mydict is a dictionary?

Wouldn't it just be easier to do isinstance(mydict, dict)?

I did some searching and found related comments in this thread: What is the best (idiomatic) way to check the type of a Python variable?, but I'm still having trouble figuring out why using abc.Mapping is preferable here than just using dict.

z33k
  • 3,280
  • 6
  • 24
  • 38
Vincent
  • 7,808
  • 13
  • 49
  • 63

3 Answers3

25

collections.abc provide a serie of Abstract Base Classes for container

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.

they allow you to check if a certain object have a behavior similar to that of the ABC you are checking without care for the actual implementation.

For example, say that you have a function F that do something according to the type of the argument, you can check if is a instance of list or tuple or dict or etc directly, and do your job, but that limit you to only have to use those, if you then make your own class that have a similar behavior to say a list, in some case you care about, and want to use it with F, you find it don't work, then you have to modify F to accept your class, but if instead you check against an ABC such modification is unneeded

Now a working example: say that you want a function that give all the elements in even position from a list, then you can do

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")

and use as

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

no problem there, but then you realize that a tuple is the same that a list in what a this function concern, you can add that check to the function too, and everything is fine, but then your friend told you he want to use your function but he is using a collections.deque and then your other friend told... see the pattern here? all the object that I mention (list, tuple, deque) have in common the same thing, and can be used in the same way by that example function, and all that behavior is compress in the ABC, so instead of isinstance(data,(list,tuple,collections.deque,...) you only need isinstance(data,abc.Sequence) and the function looks like

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']
>>> 

Now you don't need to know the actual implementation that is in use, only that it have the behavior that you want

Copperfield
  • 8,131
  • 3
  • 23
  • 29
10

The collections.abc module provides several abstract base classes that can be used to generically describe the various kinds of data structures in Python. In your example, you test if an object is an instance of the Mapping abstract class, which will be true for many classes that "work like a dictionary" (e.g. they have a __getitem__ method that takes hashable keys and return values, have keys, values and items methods, etc.). Such dict-like objects might inherit from dict but they don't need to.

The abstract types in collections.abc are implemented using the top level abc module. dict is registered as a MutableMapping (which is a subclass of Mapping) and so the isinstance check will accept a dictionary as a Mapping even though Mapping isn't an actual base class for dict.

Blckknght
  • 100,903
  • 11
  • 120
  • 169
  • This is a better answer than the accepted one, because it mentions that **collections.abc.Mapping isn't an actual base class of dict** (you can check this yourself by looking at dict.__mro__ ). Would be even better explaining what does "dict is **registered** as a MutableMapping" mean – z33k Aug 07 '20 at 09:31
  • 1
    The `register` method is documented on the page I linked. Here's [a more direct link](https://docs.python.org/3/library/abc.html#abc.ABCMeta.register) to it specifically. – Blckknght Aug 07 '20 at 17:42
4

collections.abc.Mapping is preferred because it defines abstract api for this type of containers, since dict is only an implementation of such a container. Is a bit oversimplified, but this is the key - dict is not an interface/abstract/api/...

The example of objects that are not a dict instances are MultiDict, widely used in web frameworks (e.g. aiohttp).

kwarunek
  • 12,141
  • 4
  • 43
  • 48