Reputation: 8856
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 compared to just using dict
.
Upvotes: 21
Views: 25420
Reputation: 8520
collections.abc provides a series of Abstract Base Classes for a 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 has a behavior similar to that of the ABC that you are checking without caring for the actual implementation.
For example, say that you have a function F that does 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 limits you to only being able to use those types of collection. If you then make your own class that has a similar behavior to say a list (in some cases you care about) and want to use it with F, you find it doesn't work, then you'll have to modify F to accept your class, but if instead you check against an ABC such modification is unneeded.
A working example: say that you want a function that returns the elements at an even index 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 as a list in what this function concerns so you can add that check to the function too.
Then your friend tells you he wants to use your function but he is using a collections.deque
and then your other friend needs... See the pattern here? All the objects I mention (list, tuple, deque) have in common the same thing, and can be used in the same way by that example function.
All that behavior is compressed in the ABC, so instead of isinstance(data,(list,tuple,collections.deque,...)
you only need isinstance(data,abc.Sequence)
and the function now 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 has the behavior that you want.
Upvotes: 31
Reputation: 104802
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 register
ed 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
.
Upvotes: 12
Reputation: 12597
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).
Upvotes: 5