Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to test if object is a mapping (supports **O usage)

Tags:

python

mapping

I need to test if objects read in from a file (and evaled) with ConfigParser are mappings.

Not entirely sure about the terminology here, but let me explain. Given that my object is called O it must support being used in the following manner:

def tester(**kwargs):
     print kwargs

tester(**O)

If O was not supporting the ** this would result in a TypeError, e.g. TypeError: test() argument after ** must be a mapping, not tuple.

It's a really simple scenario, but I need to know that O will work before using it and I need to be absolutely certain it won't fail. If I was testing O for being an iterable, I would use something like:

try:
   iter(O)
except:
   O = tuple()

As discussed in In Python, how do I determine if an object is iterable?

I can't find any parallel for mappings. As discussed in the same answer above, using isinstance and collections isn't an good solution.

So do I have to make my tester-function above (without the print) as my own mapping-test when loading the objects like

try:
    tester(**O)
except TypeError:
    O = {}

or does python have a built in way to test this like there is for iterables? It seems there should be one.

Edit

Actually the linked answer above never spoke against the isinstance method, should have read it better...

like image 620
deinonychusaur Avatar asked Feb 19 '14 14:02

deinonychusaur


2 Answers

Use the collections.abc.Mapping ABC:

from collections.abc import Mapping

if isinstance(O, Mapping):
    # O is a mapping

This supports any object that implements the right methods to be considered a mapping, including dict.

Demo:

>>> from collections.abc import Mapping
>>> isinstance({}, Mapping)
True
>>> isinstance((), Mapping)
False
like image 180
Martijn Pieters Avatar answered Sep 28 '22 05:09

Martijn Pieters


Some objects may not be instances of collections.Mapping and yet can be unpacked using double-star syntax(**):

import collections
def tester(**kwargs):
     print kwargs

class D:
    "http://stackoverflow.com/a/8601389/190597 (Raymond Hettinger)"
    def keys(self):
        return ['a', 'b']
    def __getitem__(self, key):
        return key.upper()

obj = D()
tester(**obj)
# {'a': 'A', 'b': 'B'}

print(isinstance(obj, collections.Mapping))
# False

The minimal interface that obj must supply is keys and __getitem__:

print(all(hasattr(obj, attr) for attr in ('keys', '__getitem__')))
# True
like image 38
unutbu Avatar answered Sep 28 '22 04:09

unutbu