tags:

views:

541

answers:

7

I have a function that may take in a number or a list of numbers. Whats the most pythonic way of checking which it is? So far I've come up with try/except block checking if i can slice the zero item ie. obj[0:0]

Edit:

I seem to have started a war of words down below by not giving enough info. For completeness let me provide more details so that I may pick and get the best answer for my situation:

I'm running Django on Python 2.6 and I'm writing a function that may take in a Django model instance or a queryset object and perform operations on it one of which involves using the filter 'in' that requires a list (the queryset input), or alternately if it is not a list then I would use the 'get' filter (the django get filter).

+1  A: 

You can use isinstance to check a variables type:

if isinstance(param, list):
   # it is a list
   print len(list)
sth
This solution prevents any custom lists that are not subclasses from list.
Georg
A: 

Just use the type method? Or am I misinterpreting the question

if type(objectname) is list:
  do something
else:
  do something else :P
Dominic Bou-Samra
This is a bad solution. A better way is isinstance() because this allows subclasses too.
Georg
+11  A: 

You don't.

This works only for Python >= 2.6. If you're targeting anything below use Alex' solution.

Python supports something called Duck Typing. You can look for certain functionality using the ABC classes.

import collections
def mymethod(myvar):
    # collections.Sqeuence to check for list capabilities
    # collections.Iterable to check for iterator capabilities
    if not isinstance(myvar, collections.Iterable):
        raise TypeError()
Georg
could someone explain the downvotes? This seems sensible, as being a "list" is a lot less interesting than being "listy".
Daren Thomas
This is the most correct answer here, which explains the downvote. (Yeah, I'm being a little sarcastic; the voting system on this site is awful.) You shouldn't be allowed to downvote at all without making a comment.
Glenn Maynard
@ Daren: I edited my answer quite heavily. (Still I think the first version wasn't _that_ bad.)
Georg
If I remember correctly ABCs like collections.Sequence were added in 2.6, which might prevent some people from usint it, but otherwise this is definitely the best solution here in my opinion *up* :-)
Horst Gutmann
It will not work on all versions, plus does Iterable means list object?
No, but collections.Sequence does which I've included in the code as a comment because I think the collections.Iterable is likely to be the one the author of the question needs.
Georg
+13  A: 
if isinstance(your_object, list):
  print("your object is a list!")

This is more Pythonic than checking with type.

Seems faster too:

>>> timeit('isinstance(x, list)', 'x = [1, 2, 3, 4]')
0.40161490440368652
>>> timeit('type(x) is list', 'x = [1, 2, 3, 4]')
0.46065497398376465
>>>
Vince
I should mention that I used "type(x) is list", and not just "type(x)" because both should return the same value (True) for the comparison to be fair.
Vince
This solution prevents any custom lists that are not subclasses from list.
Georg
@gs: You are right. Your solution below is a good clean solution (I think the cleanest on the page if indeed the OP wanted "listy" iterables rather than just a list instance). Part of the problem is in the OP's ambiguity. If he's testing whether queryset objects are lists, he should just check to see if it's an instance of QuerySet and then use len(). If he's testing whether the 'field__in' parameter is a list, my solution will work. If that parameter is more flexible, he should choose your solution.
Vince
*Your or Alex's solution (Alex's is quite elegant as well, and very specific). I never knew about basestring. Thanks, Alex!
Vince
+20  A: 

In such situations, you normally need to check for ANY iterable, not just lists -- if you're accepting lists OR numbers, rejecting (e.g) a tuple would be weird. The one kind of iterable you might want to treat as a "scalar" is a string -- in Python 2.*, this means str or unicode. So, either:

def isNonStringIterable(x):
  if isinstance(x, basestring):
    return False
  try: iter(x)
  except: return False
  else: return True

or, usually much handier:

def makeNonStringIterable(x):
  if isinstance(x, basestring):
    return (x,)
  try: return iter(x)
  except: return (x,)

where you just go for i in makeNonStringIterable(x): ...

Alex Martelli
This is the correct approach, but using the wrong tools.
Georg
@gs, given that I've been a Python committer for 8 years (and have written popular books etc on Python), I'd love you to teach me how I'm using the "wrong tools" out of all of those I helped design, implement, document, etc. Surely not (thinking about your widely-disliked answer) by not using `collections.Sequence` (which would rule out `set` without any GOOD reason), for example -- RIGHT?-)
Alex Martelli
Python mailing list entry on determining if an object is iterable: http://mail.python.org/pipermail/python-list/2006-July/566226.html
Brent Nash
@Brent, that boils down to, and I quote, "The defacto *version-independent* way to determine iterability is to call iter(ob)"... which is exactly what my code above is doing (version independent R us!-) -- so, what's your point again?-)
Alex Martelli
@ Alex: You're right. I didn't think about sets. I'm sorry if you're offended. Looks like you've got the best solution. (Depending on what the author of the question is really looking for.)
Georg
You're mixing up iterables and list-like objects. If he's looking for a list-like object, that implies random access, slicing, etc.; being iterable is just one property of lists.
Glenn Maynard
@gs, "offended"?! HA, have been for ~30 years on Usenet and the like, got rhino skin by now (all the scars, ya'know;-). "best solution"? I agree;-), but @vince got 3 times the upvotes for being 18 minutes earlier -- that's SO for you!-)
Alex Martelli
@Glenn, where do you see the need for, or use of, random-access, slicing, etc? If the arg can take a single-number object just as well as a list thereof, more often than not the function will just be iterating (which is why I say the `make` approach in my solution is better than the `is` approach in the same answer;-).
Alex Martelli
Alex Martelli
@ Alex: Thanks.
Georg
He asked for "checking if an object is a list". Maybe he actually wants to check if it's an iterable, but in my opinion there's too little (zero) information about what he's actually doing with it after he knows what it is to draw that conclusion. (If he gave use case code that showed that all he's doing if it's a list is iterating, then I'd agree with you.) We've disagreed on this sort of point before, I think; I suspect we just differ on when we choose to interpret questions loosely. *shrug*
Glenn Maynard
It is an excellent solution for the problem "Give me iterable from x", but is there any particular reason to violate pep8? Namely, CamelCase for function names, multiple statements on a single line.
J.F. Sebastian
Bare `except:` could be replaced by `except TypeError:`.
J.F. Sebastian
@J.F: yep, I followed my personal preferences (and for some case our coding standard at work, e.g. the CamelCase) rather than pep 8 (since such naming/case choices are so common throughout PyPi and even the standard library, I have little problem with it;-).
Alex Martelli
+2  A: 

I don't want to be a pest, BUT: Are you sure the query set/object is a good interface? Make two functions, like:

def fobject(i):
   # do something

def fqueryset(q):
   for obj in q:
       fobject( obj )

Might not be the pythonic way to discern an int from a list, but seems a far better design to me.

Reason being: Your function should be working on ducks. As long as it quacks, whack it. Actually picking the duck up, turning it upside down to check the markings on the belly before choosing the right club to whack it is unpythonic. Sorry. Just don't go there.

Daren Thomas
unpythonic alert! tags apart unless OP wants to do totally different things based on object, they must be same function
Anurag Uniyal
I'm not so sure about the unpythonic alert: If you have to discern based on object type (list vs. int), then you *are* doing "totally different things based on object".
Daren Thomas
+1: The original design (one function that does two separate things) is really poor. This is clearly one function for objects *and a separate function* for querysets of objects.
S.Lott
thank you for pointing this out. I've reworked the code so I dont have to do this queryset/object thing anymore by using 'filter' for both operations so the result is always a queryset. something new to learn every day :-) thanks
+1: the most desirable solution for the problem.
J.F. Sebastian
A: 

I think the way OP is doing, checking if it supports what he wants, is ok.

Simpler way in this scenario would be to not check for list which can be of many types depending on definition, you may check if input is number, do something on it else try to use it as list if that throws exception bail out.

e.g you may not want iterate over list but just wanted to append something to it if it is list else add to it

def add2(o):
    try:
        o.append(2)
    except AttributeError:
        o += 2

l=[]
n=1
s=""
add2(l)
add2(n)
add2(s) # will throw exception, let the user take care of that ;)

So bottom line is answer may vary depending on what you want to do with object

Anurag Uniyal
Your example doesn't work because numbers are immutable in python.
Georg
that is an example and what do you mean by doesn't work, who told you the purpose of the function add2, so that you can deduce it doesn't work? may be I just want to consume some cpu by adding integers
Anurag Uniyal