views:

59

answers:

2

I would like to perform an operation on an argument based on the fact that it might be a map-like object or a sequence-like object. I understand that no strategy is going to be 100% reliable for type-like checking, but I'm looking for a robust solution.

Based on this answer, I know how to determine whether something is a sequence and I can do this check after checking if the object is a map.

def ismap(arg):
   # How to implement this?

def isseq(arg):
   return hasattr(arg,"__iter__")

def operation(arg):
   if ismap(arg):
      # Do something with a dict-like object
   elif isseq(arg):
      # Do something with a sequence-like object
   else:
      # Do something else

Because a sequence can be seen as a map where keys are integers, should I just try to find a key that is not an integer? Or maybe I could look at the string representation? or...?

UPDATE

I selected SilentGhost's answer because it looks like the most "correct" one, but for my needs, here is the solution I ended up implementing:

if hasattr(arg, 'keys') and hasattr(arg, '__getitem__'):
   # Do something with a map
elif hasattr(arg, '__iter__'):
   # Do something with a sequence/iterable
else:
   # Do something else

Essentially, I don't want to rely on an ABC because there are many custom classes that behave like sequences and dictionary but that still do not extend the python collections ABCs (see @Manoj comment). I thought the keys attribute (mentioned by someone who removed his/her answer) was a good enough check for mappings.

Classes extending the Sequence and Mapping ABCs will work with this solution as well.

+1  A: 

Sequences have an __add__ method that implements the + operator. Maps do not have that method, since adding to a map requires both a key and a value, and the + operator only has one right-hand side.

So you may try:

def ismap(arg):
    return isseq(arg) and not hasattr(arg, "__add__")
Frédéric Hamidi
+5  A: 
>>> from collections import Mapping, Sequence
>>> isinstance('ac', Sequence)
True
>>> isinstance('ac', Mapping)
False
>>> isinstance({3:42}, Mapping)
True
>>> isinstance({3:42}, Sequence)
False

collections abstract base classes (ABCs)

SilentGhost
How about custom classes? For e.g. Django's `QuerySet`? It behaves like a sequence but `isinstance(a_queryset, Sequence)` returns `False`.
Manoj Govindan
@Manoj: they might need to fix inheritance? In what sense does it "behave like a sequence"?
SilentGhost
why the downvote?
SilentGhost
@SilentGhost: Hmmm. You are right. I guess designers will have to start by subclassing the appropriate type if they expect such operations.
Manoj Govindan
@SilentGhost: I didn't down vote you! In fact I did the opposite :)
Manoj Govindan
@SilentGhost: you can iterate over a queryset; it can be indexed (positive indexes only); it supports `len`.
Manoj Govindan
@Manoj: I still would be interested in what you meant by "behaves like a sequence". The docs have a table describing what it means to be an instance of each ABC.
SilentGhost
@Manoj: does it support `.index` and `.count` methods?
SilentGhost
@SilentGhost: `.count()`: yes. `.index()`: no.
Manoj Govindan
The downvote was me, but I changed my mind. It's a good answer. +1
aaronasterling