By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
448,573 Members | 1,223 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 448,573 IT Pros & Developers. It's quick & easy.

is parameter an iterable?

P: n/a
py
I have function which takes an argument. My code needs that argument
to be an iterable (something i can loop over)...so I dont care if its a
list, tuple, etc. So I need a way to make sure that the argument is an
iterable before using it. I know I could do...

def foo(inputVal):
if isinstance(inputVal, (list, tuple)):
for val in inputVal:
# do stuff

....however I want to cover any iterable since i just need to loop over
it.

any suggestions?

Nov 22 '05 #1
Share this Question
Share on Google+
70 Replies


P: n/a
On 15 Nov 2005 11:01:48 -0800,
"py" <co*******@gmail.com> wrote:
I have function which takes an argument. My code needs that argument
to be an iterable (something i can loop over)...so I dont care if its a
list, tuple, etc. So I need a way to make sure that the argument is an
iterable before using it. I know I could do... def foo(inputVal):
if isinstance(inputVal, (list, tuple)):
for val in inputVal:
# do stuff ...however I want to cover any iterable since i just need to loop over
it. any suggestions?


Just do it. If one of foo's callers passes in a non-iterable, foo will
raise an exception, and you'll catch it during testing. Watch out for
strings, though:
def foo(i): ... for j in i:
... print j foo([1, 3, 4, 5, 6, 7]) 1
3
4
5
6
7 foo("hello")

h
e
l
l
o

Regards,
Dan

--
Dan Sommers
<http://www.tombstonezero.net/dan/>
Nov 22 '05 #2

P: n/a
On Tue, 2005-11-15 at 11:01 -0800, py wrote:
I have function which takes an argument. My code needs that argument
to be an iterable (something i can loop over)...so I dont care if its a
list, tuple, etc. So I need a way to make sure that the argument is an
iterable before using it. I know I could do...

def foo(inputVal):
if isinstance(inputVal, (list, tuple)):
for val in inputVal:
# do stuff

...however I want to cover any iterable since i just need to loop over
it.

any suggestions?


You could probably get away with

if hasattr(inputVal, '__getitem__')
Nov 22 '05 #3

P: n/a
py
Dan Sommers wrote:
Just do it. If one of foo's callers passes in a non-iterable, foo will
raise an exception, and you'll catch it during testing


That's exactly what I don't want. I don't want an exception, instead I
want to check to see if it's an iterable....if it is continue, if not
return an error code. I can't catch it during testing since this is
going to be used by other people.

Thanks for the suggestion though.

Nov 22 '05 #4

P: n/a
marduk wrote:
On Tue, 2005-11-15 at 11:01 -0800, py wrote:
I have function which takes an argument. My code needs that argument
to be an iterable (something i can loop over)...so I dont care if its a
list, tuple, etc. So I need a way to make sure that the argument is an
iterable before using it. I know I could do...

def foo(inputVal):
if isinstance(inputVal, (list, tuple)):
for val in inputVal:
# do stuff

...however I want to cover any iterable since i just need to loop over
it.

any suggestions?


You could probably get away with

if hasattr(inputVal, '__getitem__')


No, you probably couldn't.

##################
def g(s): for i in xrange(s):
yield i+s

m = g(5)
hasattr(m, '__getitem__')

False
###################

I'd do something like:

#####################
def foo(inputVal):
try:
iter(inputVal) # Can you change it into an interator?
except TypeError:
# Return Error Code
else:
for val in inputVal:
# do stuff
#######################

Again, you'll have to be careful about strings.
Nov 22 '05 #5

P: n/a
py wrote:
Dan Sommers wrote:
Just do it. If one of foo's callers passes in a non-iterable, foo will
raise an exception, and you'll catch it during testing
That's exactly what I don't want. I don't want an exception, instead I
want to check to see if it's an iterable....if it is continue, if not
return an error code.


Why return an error code? Just pass along the exception (i.e. do nothing
special). Python's exception mechanism is far superior to error codes.
Don't try to fight the language.
I can't catch it during testing since this is
going to be used by other people.


Then *they'll* catch it during testing.

--
Robert Kern
rk***@ucsd.edu

"In the fields of hell where the grass grows high
Are the graves of dreams allowed to die."
-- Richard Harter

Nov 22 '05 #6

P: n/a
py
Thanks for the replies. I agree with Jean-Paul Calderone's
suggestion...let the exception be raised.

Thanks.

Nov 22 '05 #7

P: n/a
Hi!

py wrote:
Dan Sommers wrote:
Just do it. If one of foo's callers passes in a non-iterable, foo will
raise an exception, and you'll catch it during testing

That's exactly what I don't want. I don't want an exception, instead I
want to check to see if it's an iterable....if it is continue, if not
return an error code. I can't catch it during testing since this is
going to be used by other people.


Note that using error codes is usually quite "unpythonic", the way to
signal that something is exceptional (not necessarily wrong) is, well,
an exception.

Anyway, one way to solve this is the following:

def foo(input_val):
try:
iterator = iter(input_val)
except TypeError:
# do non-iterable stuff
else:
for val in iterator:
# do loop stuff

Cheers,

Carl Friedrich Bolz

Nov 22 '05 #8

P: n/a
In article <11*********************@g49g2000cwa.googlegroups. com>,
py <co*******@gmail.com> wrote:
I have function which takes an argument. My code needs that argument
to be an iterable (something i can loop over)...so I dont care if its a
list, tuple, etc.


My first thought was to just write your loop inside a try block and
catch the error if it wasn't iterable, but then I noticed that you get:

TypeError: iteration over non-sequence

I was kind of hoping for a more specific exception than TypeError.
You can't tell the difference between:

try:
for i in 5:
print i + 1
except TypeError:
print "non-iterable"

and

try:
for i in ["one", "two", "three"]:
print i + 1
except TypeError:
print "can't add string and integer"

Unfortunately, you can't just try it in a bodyless loop to prove that
you can iterate before doing the real thing because not all iterators
are idempotent.

It's an interesting problem.
Nov 22 '05 #9

P: n/a
On 2005-11-15, py <co*******@gmail.com> wrote:
Dan Sommers wrote:
Just do it. If one of foo's callers passes in a non-iterable, foo will
raise an exception, and you'll catch it during testing


That's exactly what I don't want. I don't want an exception, instead I
want to check to see if it's an iterable....if it is continue, if not
return an error code. I can't catch it during testing since this is
going to be used by other people.


If I were those other people, and you decided to return error
codes to me instead of passing up the proper exception (the
good, Pythonic thing to do), I'd be fairly pissed off at you.

An exception is the _right_ way to let the caller know
something is wrong.

--
Grant Edwards grante Yow! I smell like a wet
at reducing clinic on Columbus
visi.com Day!
Nov 22 '05 #10

P: n/a
On 15 Nov 2005 11:26:23 -0800 in comp.lang.python, "py"
<co*******@gmail.com> wrote:
Dan Sommers wrote:
Just do it. If one of foo's callers passes in a non-iterable, foo will
raise an exception, and you'll catch it during testing


That's exactly what I don't want. I don't want an exception, instead I
want to check to see if it's an iterable....if it is continue, if not
return an error code. I can't catch it during testing since this is
going to be used by other people.


Then catch the exception yourself.
def foo2(i): try:
for j in i:
print j
print "Success!"
return 0
except TypeError, e:
print "Bad foo. No donut.", e
return -1

joe = foo2([1,3,5,7,9]) 1
3
5
7
9
Success! print joe 0 bob = foo2(2) Bad foo. No donut. iteration over non-sequence print bob -1


Regards,
-=Dave

--
Change is inevitable, progress is not.
Nov 22 '05 #11

P: n/a
Maybe this helps:
import types
def foo(inputVal): inValType = type(inputVal)
if inValType==types.ListType or inValType==types.TupleType:
for val in inputVal:
print val
else:
print 'Wrong input Type'

list = [1,2,3,4]
foo(list) 1
2
3
4 tup = ('a', 'b', 'c')
foo(tup) a
b
c foo(9) Wrong input Type


Nov 22 '05 #12

P: n/a
On Tue, 15 Nov 2005 14:06:45 -0500, Dan Sommers wrote:
On 15 Nov 2005 11:01:48 -0800,
"py" <co*******@gmail.com> wrote:
I have function which takes an argument. My code needs that argument
to be an iterable (something i can loop over)...so I dont care if its a
list, tuple, etc. So I need a way to make sure that the argument is an
iterable before using it. I know I could do...

....
Just do it. If one of foo's callers passes in a non-iterable, foo will
raise an exception, and you'll catch it during testing.


It isn't during testing so much that he needs to watch out, as run-time.
You have three options:

(1) The "if the user is silly enough to pass a non-iterable to my
function, they deserve to have it fail" school of thought. Dan's advise
comes under this heading.

(2) The "if the user passes a non-iterable, I want to catch the exception
and recover gracefully" school of thought. Recovering gracefully may mean
raising your own exception, with a user friendly error message ("Hey
butthead, pass an iterable willya!!!"), or it may mean just catching the
exception and doing something else, depending on the needs of your
program. (For example, as a user-friendly feature, you might want to treat
ints as if they were iterables of one item only, so users can pass 5 as an
argument instead of [5].)

For the second case, what you want to do is wrap your code in a
try...except block and catch the exception raised when a non-iterator is
passed as an argument.

Which exception is that? I leave that as an exercise for the reader.
(Hint: Just Do It and read the traceback Python prints.)

Actually, I think "Just Do It" might make a good motto for Python.
--
Steven

Nov 22 '05 #13

P: n/a
How about hasattr("__iter__")?

Regards,
Karsten.

Nov 22 '05 #14

P: n/a
lm*******@gmail.com wrote:
Maybe this helps:
import types
def foo(inputVal):

inValType = type(inputVal)
if inValType==types.ListType or inValType==types.TupleType:


And what of user-created types that are iterable?

What of user-created iterable types that don't inherit from any of the
built-in iterable types?

--
\ "A good politician is quite as unthinkable as an honest |
`\ burglar." -- Henry L. Mencken |
_o__) |
Ben Finney
Nov 22 '05 #15

P: n/a
On Tue, 15 Nov 2005 11:26:23 -0800, py wrote:
Dan Sommers wrote:
Just do it. If one of foo's callers passes in a non-iterable, foo will
raise an exception, and you'll catch it during testing


That's exactly what I don't want. I don't want an exception, instead I
want to check to see if it's an iterable....if it is continue, if not
return an error code. I can't catch it during testing since this is
going to be used by other people.


That would be the Look Before You Leap school of thought.

That is rarely the best way of doing things in Python. (Note I said
*rarely*, not *never*.) If you insist, you would have to do something like
this:

def is_iterable(obj):
"""Returns True if obj is *probably* an iterable and False
otherwise. Not that some weird custom objects that break the
rules may give incorrect results.
"""
if type(obj) in (type([]), type(()), type("")):
return True
elif hasattr(obj, next) and callable(obj.next):
return True
elif hasattr(obj, __getitem__):
return True
else:
# I don't think I missed anything...
return False

But in fact there is an easier way:

def is_iterable(obj):
"""Just Do It."""
try:
for item in obj:
break
except TypeError:
return False
return True

That works, but it has a side-effect: if you pass it an iterable that gets
consumed, you now have used up the first item of it (e.g. if you a
reading lines from a file, you've just used the first line and won't get
it back unless you reset the file pointer).

So do your test inside your code:

def foo(inputVal):
try:
for val in inputVal:
# do stuff
except TypeError, msg:
if msg == "iteration over non-sequence":
# handle non-iterable case
else:
# some other TypeError is a bug, so re-raise the exception
raise


Nov 22 '05 #16

P: n/a
Roy Smith <ro*@panix.com> wrote:
...
TypeError: iteration over non-sequence

I was kind of hoping for a more specific exception than TypeError.
You can't tell the difference between:

try:
for i in 5:
print i + 1
except TypeError:
print "non-iterable"

and

try:
for i in ["one", "two", "three"]:
print i + 1
except TypeError:
print "can't add string and integer"

Unfortunately, you can't just try it in a bodyless loop to prove that
you can iterate before doing the real thing because not all iterators
are idempotent.


It's not hard...:

try:
_it = iter(whatever)
except TypeError:
print 'non-iterable'
else:
for i in _it: # etc, etc

Alex
Nov 22 '05 #17

P: n/a
Roy Smith <ro*@panix.com> wrote:
...
TypeError: iteration over non-sequence

I was kind of hoping for a more specific exception than TypeError.
You can't tell the difference between:

try:
for i in 5:
print i + 1
except TypeError:
print "non-iterable"

and

try:
for i in ["one", "two", "three"]:
print i + 1
except TypeError:
print "can't add string and integer"

Unfortunately, you can't just try it in a bodyless loop to prove that
you can iterate before doing the real thing because not all iterators
are idempotent.


It's not hard...:

try:
_it = iter(whatever)
except TypeError:
print 'non-iterable'
else:
for i in _it: # etc, etc

Alex
Nov 22 '05 #18

P: n/a
Roy Smith wrote:
You can't tell the difference between:

try:
for i in 5:
print i + 1
except TypeError:
print "non-iterable"

and

try:
for i in ["one", "two", "three"]:
print i + 1
except TypeError:
print "can't add string and integer"

try:
for item in obj:
do_stuff(item)
except TypeError, msg:
if msg == "iteration over non-sequence":
handle_non_iterator()
else:
# re-raise the exception
raise

--
Steven.

Nov 22 '05 #19

P: n/a
Roy Smith wrote:
You can't tell the difference between:

try:
for i in 5:
print i + 1
except TypeError:
print "non-iterable"

and

try:
for i in ["one", "two", "three"]:
print i + 1
except TypeError:
print "can't add string and integer"

try:
for item in obj:
do_stuff(item)
except TypeError, msg:
if msg == "iteration over non-sequence":
handle_non_iterator()
else:
# re-raise the exception
raise

--
Steven.

Nov 22 '05 #20

P: n/a
lm*******@gmail.com wrote:
Maybe this helps:


Unfortunately, it doesn't. If you read the sample code
the original poster first gave, you will see that Py's
example is virtually the same as yours:

[original code]
def foo(inputVal):
if isinstance(inputVal, (list, tuple)):
for val in inputVal:
# do stuff

It uses the same program logic, only the implementation
is different: your code tests the object's type, and
compares it to the type of list and tuple. Py's code
tests the object's type, comparing it to lists and tuples.

Type testing gives less information than using
isinstance, and takes more work to do it. For instance,
Py's code will correctly work with subclasses of lists,
yours will wrongly reject them.

Check Py's requirements carefully:

"I have function which takes an argument. My code
needs that argument to be an iterable (something i can
loop over)...so I dont care if its a list, tuple, etc."

Notice the "etc"? There are lots of iterables other
than lists and tuples. There are xrange objects, files,
strings, buffers, sub-classes of all of those, classes
that implement __getitem__(), classes that implement
next(), and probably more things that I have forgotten.

Your code doesn't check for any of those.
--
Steven.

Nov 22 '05 #21

P: n/a
lm*******@gmail.com wrote:
Maybe this helps:


Unfortunately, it doesn't. If you read the sample code
the original poster first gave, you will see that Py's
example is virtually the same as yours:

[original code]
def foo(inputVal):
if isinstance(inputVal, (list, tuple)):
for val in inputVal:
# do stuff

It uses the same program logic, only the implementation
is different: your code tests the object's type, and
compares it to the type of list and tuple. Py's code
tests the object's type, comparing it to lists and tuples.

Type testing gives less information than using
isinstance, and takes more work to do it. For instance,
Py's code will correctly work with subclasses of lists,
yours will wrongly reject them.

Check Py's requirements carefully:

"I have function which takes an argument. My code
needs that argument to be an iterable (something i can
loop over)...so I dont care if its a list, tuple, etc."

Notice the "etc"? There are lots of iterables other
than lists and tuples. There are xrange objects, files,
strings, buffers, sub-classes of all of those, classes
that implement __getitem__(), classes that implement
next(), and probably more things that I have forgotten.

Your code doesn't check for any of those.
--
Steven.

Nov 22 '05 #22

P: n/a
On Wed, 16 Nov 2005 13:12:38 +1100,
Steven D'Aprano <st***@REMOVEMEcyber.com.au> wrote:
try:
for item in obj:
do_stuff(item)
except TypeError, msg:
if msg == "iteration over non-sequence":
handle_non_iterator()
else:
# re-raise the exception
raise


But what it do_stuff tries to iterate over a non-sequence?

Regards,
Dan

--
Dan Sommers
<http://www.tombstonezero.net/dan/>
Nov 22 '05 #23

P: n/a
On Wed, 16 Nov 2005 13:12:38 +1100,
Steven D'Aprano <st***@REMOVEMEcyber.com.au> wrote:
try:
for item in obj:
do_stuff(item)
except TypeError, msg:
if msg == "iteration over non-sequence":
handle_non_iterator()
else:
# re-raise the exception
raise


But what it do_stuff tries to iterate over a non-sequence?

Regards,
Dan

--
Dan Sommers
<http://www.tombstonezero.net/dan/>
Nov 22 '05 #24

P: n/a
In article <43**************@REMOVEMEcyber.com.au>,
Steven D'Aprano <st***@REMOVEMEcyber.com.au> wrote:
try:
for item in obj:
do_stuff(item)
except TypeError, msg:
if msg == "iteration over non-sequence":
handle_non_iterator()
else:
# re-raise the exception
raise


That's the obvious solution, but it's a poor one because it depends on an
undocumented feature of the language. What's documented is that TypeError
is raised; it's not documented what the text of the message will be.
Nov 22 '05 #25

P: n/a
In article <43**************@REMOVEMEcyber.com.au>,
Steven D'Aprano <st***@REMOVEMEcyber.com.au> wrote:
try:
for item in obj:
do_stuff(item)
except TypeError, msg:
if msg == "iteration over non-sequence":
handle_non_iterator()
else:
# re-raise the exception
raise


That's the obvious solution, but it's a poor one because it depends on an
undocumented feature of the language. What's documented is that TypeError
is raised; it's not documented what the text of the message will be.
Nov 22 '05 #26

P: n/a
On Tue, 15 Nov 2005 21:54:07 -0500, Dan Sommers wrote:
On Wed, 16 Nov 2005 13:12:38 +1100,
Steven D'Aprano <st***@REMOVEMEcyber.com.au> wrote:
try:
for item in obj:
do_stuff(item)
except TypeError, msg:
if msg == "iteration over non-sequence":
handle_non_iterator()
else:
# re-raise the exception
raise


But what it do_stuff tries to iterate over a non-sequence?


I can think of two solutions:

(1) Write do_stuff carefully, and test it thoroughly on its own. If there
are no bugs in do_stuff, you can be sure it isn't going to try to iterate
over a non-iterator. This is a good argument for writing small,
encapsulated, easily analysed and debugged pieces of code.

(2) Muck about writing fragile, possibly buggy code:

# untested
try:
for item in obj:
flag = False
try:
do_stuff(item)
except TypeError, msg:
if msg == "iteration over non-sequence":
flag = True
# re-raise the exception
raise
except TypeError, msg:
if flag:
raise
if msg == "iteration over non-sequence":
handle_non_iterator()
else:
raise

I think you can guess where my preference lies.
--
Steven.

Nov 22 '05 #27

P: n/a
On Tue, 15 Nov 2005 21:54:07 -0500, Dan Sommers wrote:
On Wed, 16 Nov 2005 13:12:38 +1100,
Steven D'Aprano <st***@REMOVEMEcyber.com.au> wrote:
try:
for item in obj:
do_stuff(item)
except TypeError, msg:
if msg == "iteration over non-sequence":
handle_non_iterator()
else:
# re-raise the exception
raise


But what it do_stuff tries to iterate over a non-sequence?


I can think of two solutions:

(1) Write do_stuff carefully, and test it thoroughly on its own. If there
are no bugs in do_stuff, you can be sure it isn't going to try to iterate
over a non-iterator. This is a good argument for writing small,
encapsulated, easily analysed and debugged pieces of code.

(2) Muck about writing fragile, possibly buggy code:

# untested
try:
for item in obj:
flag = False
try:
do_stuff(item)
except TypeError, msg:
if msg == "iteration over non-sequence":
flag = True
# re-raise the exception
raise
except TypeError, msg:
if flag:
raise
if msg == "iteration over non-sequence":
handle_non_iterator()
else:
raise

I think you can guess where my preference lies.
--
Steven.

Nov 22 '05 #28

P: n/a
On Tue, 15 Nov 2005 21:59:44 -0500, Roy Smith wrote:
In article <43**************@REMOVEMEcyber.com.au>,
Steven D'Aprano <st***@REMOVEMEcyber.com.au> wrote:
try:
for item in obj:
do_stuff(item)
except TypeError, msg:
if msg == "iteration over non-sequence":
handle_non_iterator()
else:
# re-raise the exception
raise


That's the obvious solution, but it's a poor one because it depends on an
undocumented feature of the language. What's documented is that TypeError
is raised; it's not documented what the text of the message will be.

It would be nice if The Powers That Be would document the specific error
messages, so that we could rely on them in total safety.

But even without that, the consequences aren't especially dire. What
happens if the error message changes in some future version of Python?
Then the error won't be caught, the exception will be re-raised, and your
testing will catch it immediately.
It isn't the ideal solution, but it is a solution.
--
Steven.

Nov 22 '05 #29

P: n/a
On Tue, 15 Nov 2005 21:59:44 -0500, Roy Smith wrote:
In article <43**************@REMOVEMEcyber.com.au>,
Steven D'Aprano <st***@REMOVEMEcyber.com.au> wrote:
try:
for item in obj:
do_stuff(item)
except TypeError, msg:
if msg == "iteration over non-sequence":
handle_non_iterator()
else:
# re-raise the exception
raise


That's the obvious solution, but it's a poor one because it depends on an
undocumented feature of the language. What's documented is that TypeError
is raised; it's not documented what the text of the message will be.

It would be nice if The Powers That Be would document the specific error
messages, so that we could rely on them in total safety.

But even without that, the consequences aren't especially dire. What
happens if the error message changes in some future version of Python?
Then the error won't be caught, the exception will be re-raised, and your
testing will catch it immediately.
It isn't the ideal solution, but it is a solution.
--
Steven.

Nov 22 '05 #30

P: n/a
Steven D'Aprano <st***@REMOVETHIScyber.com.au> wrote in
news:pa****************************@REMOVETHIScybe r.com.au:
def foo(inputVal):
try:
for val in inputVal:
# do stuff
except TypeError, msg:
if msg == "iteration over non-sequence":
# handle non-iterable case
else:
# some other TypeError is a bug, so re-raise the
exception raise


Does this in fact work on your system? On mine (2.4.1 (#65, Mar 30
2005, 09:13:57) [MSC v.1310 32 bit (Intel)]), it doesn't seem to. I
tried
if msg.find("iteration over non-sequence") >= 0:
.... but I got a traceback, and
AttributeError: TypeError instance has no attribute 'find'
.... which leads me to belive that 'msg' is not type(str). It can be
coerced (str(msg).find works as expected). But what exactly is msg?
It appears to be of <type 'instance'>, and does not test equal to a
string. This is not the least surprise to me.

--
rzed
Nov 22 '05 #31

P: n/a
Steven D'Aprano <st***@REMOVETHIScyber.com.au> wrote in
news:pa****************************@REMOVETHIScybe r.com.au:
def foo(inputVal):
try:
for val in inputVal:
# do stuff
except TypeError, msg:
if msg == "iteration over non-sequence":
# handle non-iterable case
else:
# some other TypeError is a bug, so re-raise the
exception raise


Does this in fact work on your system? On mine (2.4.1 (#65, Mar 30
2005, 09:13:57) [MSC v.1310 32 bit (Intel)]), it doesn't seem to. I
tried
if msg.find("iteration over non-sequence") >= 0:
.... but I got a traceback, and
AttributeError: TypeError instance has no attribute 'find'
.... which leads me to belive that 'msg' is not type(str). It can be
coerced (str(msg).find works as expected). But what exactly is msg?
It appears to be of <type 'instance'>, and does not test equal to a
string. This is not the least surprise to me.

--
rzed
Nov 22 '05 #32

P: n/a
In article <Xn*********************@63.223.7.253>,
Rick Wotnaz <de*****@wtf.com> wrote:
Steven D'Aprano <st***@REMOVETHIScyber.com.au> wrote in
news:pa****************************@REMOVETHIScybe r.com.au:
def foo(inputVal):
try:
for val in inputVal:
# do stuff
except TypeError, msg:
if msg == "iteration over non-sequence":
# handle non-iterable case
else:
# some other TypeError is a bug, so re-raise the
exception raise


Does this in fact work on your system? On mine (2.4.1 (#65, Mar 30
2005, 09:13:57) [MSC v.1310 32 bit (Intel)]), it doesn't seem to. I
tried
if msg.find("iteration over non-sequence") >= 0:
... but I got a traceback, and
AttributeError: TypeError instance has no attribute 'find'
... which leads me to belive that 'msg' is not type(str). It can be
coerced (str(msg).find works as expected). But what exactly is msg?
It appears to be of <type 'instance'>, and does not test equal to a
string. This is not the least surprise to me.


It's an easy experiment to do:

-------------------
Roy-Smiths-Computer:play$ cat ex.py
#!/usr/bin/env python

try:
1 + "foo"
except TypeError, msg:
print type(msg)
print msg
print repr(msg)
print dir(msg)
Roy-Smiths-Computer:play$ py ex.py
<type 'instance'>
unsupported operand type(s) for +: 'int' and 'str'
<exceptions.TypeError instance at 0x36d968>
['__doc__', '__getitem__', '__init__', '__module__', '__str__', 'args']
---------------------
Nov 22 '05 #33

P: n/a
In article <Xn*********************@63.223.7.253>,
Rick Wotnaz <de*****@wtf.com> wrote:
Steven D'Aprano <st***@REMOVETHIScyber.com.au> wrote in
news:pa****************************@REMOVETHIScybe r.com.au:
def foo(inputVal):
try:
for val in inputVal:
# do stuff
except TypeError, msg:
if msg == "iteration over non-sequence":
# handle non-iterable case
else:
# some other TypeError is a bug, so re-raise the
exception raise


Does this in fact work on your system? On mine (2.4.1 (#65, Mar 30
2005, 09:13:57) [MSC v.1310 32 bit (Intel)]), it doesn't seem to. I
tried
if msg.find("iteration over non-sequence") >= 0:
... but I got a traceback, and
AttributeError: TypeError instance has no attribute 'find'
... which leads me to belive that 'msg' is not type(str). It can be
coerced (str(msg).find works as expected). But what exactly is msg?
It appears to be of <type 'instance'>, and does not test equal to a
string. This is not the least surprise to me.


It's an easy experiment to do:

-------------------
Roy-Smiths-Computer:play$ cat ex.py
#!/usr/bin/env python

try:
1 + "foo"
except TypeError, msg:
print type(msg)
print msg
print repr(msg)
print dir(msg)
Roy-Smiths-Computer:play$ py ex.py
<type 'instance'>
unsupported operand type(s) for +: 'int' and 'str'
<exceptions.TypeError instance at 0x36d968>
['__doc__', '__getitem__', '__init__', '__module__', '__str__', 'args']
---------------------
Nov 22 '05 #34

P: n/a
Rick Wotnaz wrote.
... which leads me to belive that 'msg' is not type(str). It can be
coerced (str(msg).find works as expected). But what exactly is msg?
It appears to be of <type 'instance'>, and does not test equal to a
string.


it's an instance of the exception type, of course.

:::

if you do

raise SomeError, value

Python will actually do

raise SomeError(value)

(that is, create a SomeError exception and pass the value as its
first argument).

you can use either form in your code (I prefer the latter myself).

:::

as for catching the exceptions, if you do

try:
...
except SomeError, v:
...

Python will treat this as

try:
...
except:
# some exception occurred
typ = sys.exc_type
exc = sys.exc_value
if issubclass(typ, SomeError):
v = exc
...
else:
raise # propagate!

(where typ and exc are internal variables)

</F>

Nov 22 '05 #35

P: n/a
Rick Wotnaz wrote.
... which leads me to belive that 'msg' is not type(str). It can be
coerced (str(msg).find works as expected). But what exactly is msg?
It appears to be of <type 'instance'>, and does not test equal to a
string.


it's an instance of the exception type, of course.

:::

if you do

raise SomeError, value

Python will actually do

raise SomeError(value)

(that is, create a SomeError exception and pass the value as its
first argument).

you can use either form in your code (I prefer the latter myself).

:::

as for catching the exceptions, if you do

try:
...
except SomeError, v:
...

Python will treat this as

try:
...
except:
# some exception occurred
typ = sys.exc_type
exc = sys.exc_value
if issubclass(typ, SomeError):
v = exc
...
else:
raise # propagate!

(where typ and exc are internal variables)

</F>

Nov 22 '05 #36

P: n/a
"Fredrik Lundh" <fr*****@pythonware.com> wrote in
news:ma**************************************@pyth on.org:
Rick Wotnaz wrote.
... which leads me to belive that 'msg' is not type(str). It
can be coerced (str(msg).find works as expected). But what
exactly is msg? It appears to be of <type 'instance'>, and does
not test equal to a string.


it's an instance of the exception type, of course.

:::

if you do

raise SomeError, value

Python will actually do

raise SomeError(value)

(that is, create a SomeError exception and pass the value as its
first argument).

you can use either form in your code (I prefer the latter
myself).

:::

as for catching the exceptions, if you do

try:
...
except SomeError, v:
...

Python will treat this as

try:
...
except:
# some exception occurred
typ = sys.exc_type
exc = sys.exc_value
if issubclass(typ, SomeError):
v = exc
...
else:
raise # propagate!

(where typ and exc are internal variables)


Thank you (and Roy Smith) for helping to clarify this. I see that
my mental image of an Exception (which, I admit, was not based on
extensive R'ing of TFM) was way off. Judging by Steven D'Aprano's
code sample, I'm not the only one who was mistaken about the nature
of v in your example. I'd always assumed it was the human-
readable string associated with the TypeError. Wrong, I see.

--
rzed
Nov 22 '05 #37

P: n/a
"Fredrik Lundh" <fr*****@pythonware.com> wrote in
news:ma**************************************@pyth on.org:
Rick Wotnaz wrote.
... which leads me to belive that 'msg' is not type(str). It
can be coerced (str(msg).find works as expected). But what
exactly is msg? It appears to be of <type 'instance'>, and does
not test equal to a string.


it's an instance of the exception type, of course.

:::

if you do

raise SomeError, value

Python will actually do

raise SomeError(value)

(that is, create a SomeError exception and pass the value as its
first argument).

you can use either form in your code (I prefer the latter
myself).

:::

as for catching the exceptions, if you do

try:
...
except SomeError, v:
...

Python will treat this as

try:
...
except:
# some exception occurred
typ = sys.exc_type
exc = sys.exc_value
if issubclass(typ, SomeError):
v = exc
...
else:
raise # propagate!

(where typ and exc are internal variables)


Thank you (and Roy Smith) for helping to clarify this. I see that
my mental image of an Exception (which, I admit, was not based on
extensive R'ing of TFM) was way off. Judging by Steven D'Aprano's
code sample, I'm not the only one who was mistaken about the nature
of v in your example. I'd always assumed it was the human-
readable string associated with the TypeError. Wrong, I see.

--
rzed
Nov 22 '05 #38

P: n/a
On Wed, 16 Nov 2005 09:06:01 -0500, Rick Wotnaz wrote:

[cutting to the important bit]
except TypeError, msg:
if msg == "iteration over non-sequence":
# handle non-iterable case
Does this in fact work on your system? On mine (2.4.1 (#65, Mar 30
2005, 09:13:57) [MSC v.1310 32 bit (Intel)]), it doesn't seem to.


Dammit, that will teach me not to test my code before posting.

No it doesn't: msg is an object of type exceptions.TypeError.

The easy fix is to just coerce it to a string:

if str(msg) == "iteration over non-sequence":

which *does* work on my system. But perhaps a better way is to do this:
# Create an instance of the exception you expect:
try:
for i in 0:
pass
except TypeError, ITER_OVER_NON_SEQ:
pass
# Now run your code...
try:
...blah blah blah...
except TypeError, msg
if str(msg) == str(ITER_OVER_NON_SEQ):
...blah blah blah...

This means we're no longer assuming what the error message will be,
which makes our code a lot more future-proof and implementation-proof: if
some version of Python changes the error string from "iteration over
non-sequence" to something else, the code should continue to work
correctly.
--
Steven.

Nov 22 '05 #39

P: n/a
On Wed, 16 Nov 2005 09:06:01 -0500, Rick Wotnaz wrote:

[cutting to the important bit]
except TypeError, msg:
if msg == "iteration over non-sequence":
# handle non-iterable case
Does this in fact work on your system? On mine (2.4.1 (#65, Mar 30
2005, 09:13:57) [MSC v.1310 32 bit (Intel)]), it doesn't seem to.


Dammit, that will teach me not to test my code before posting.

No it doesn't: msg is an object of type exceptions.TypeError.

The easy fix is to just coerce it to a string:

if str(msg) == "iteration over non-sequence":

which *does* work on my system. But perhaps a better way is to do this:
# Create an instance of the exception you expect:
try:
for i in 0:
pass
except TypeError, ITER_OVER_NON_SEQ:
pass
# Now run your code...
try:
...blah blah blah...
except TypeError, msg
if str(msg) == str(ITER_OVER_NON_SEQ):
...blah blah blah...

This means we're no longer assuming what the error message will be,
which makes our code a lot more future-proof and implementation-proof: if
some version of Python changes the error string from "iteration over
non-sequence" to something else, the code should continue to work
correctly.
--
Steven.

Nov 22 '05 #40

P: n/a
Steven D'Aprano wrote:
This means we're no longer assuming what the error message will be,
which makes our code a lot more future-proof and implementation-proof: if
some version of Python changes the error string from "iteration over
non-sequence" to something else, the code should continue to work
correctly.


Alex has already posted the right way to do this. can you please stop

</F>

Nov 22 '05 #41

P: n/a
Steven D'Aprano wrote:
This means we're no longer assuming what the error message will be,
which makes our code a lot more future-proof and implementation-proof: if
some version of Python changes the error string from "iteration over
non-sequence" to something else, the code should continue to work
correctly.


Alex has already posted the right way to do this. can you please stop

</F>

Nov 22 '05 #42

P: n/a
> Alex has already posted the right way to do this. can you please stop

posting crappy non-solutions to a problem that has a very simple solution (split
things up), that should be obvious to anyone who didn't sleep through exceptions
101.

</F>

Nov 22 '05 #43

P: n/a
> Alex has already posted the right way to do this. can you please stop

posting crappy non-solutions to a problem that has a very simple solution (split
things up), that should be obvious to anyone who didn't sleep through exceptions
101.

</F>

Nov 22 '05 #44

P: n/a
On Wed, 16 Nov 2005 17:39:57 +0100, "Fredrik Lundh" <fr*****@pythonware.com> wrote:
Rick Wotnaz wrote.
... which leads me to belive that 'msg' is not type(str). It can be
coerced (str(msg).find works as expected). But what exactly is msg?
It appears to be of <type 'instance'>, and does not test equal to a
string.
it's an instance of the exception type, of course.

:::

if you do

raise SomeError, value

Python will actually do

raise SomeError(value)


Depending on what you mean by "actually" I guess ...
(I'm sure you know this and more ;-)
dis.dis(compile('raise SomeError, value','','exec')) 1 0 LOAD_NAME 0 (SomeError)
3 LOAD_NAME 1 (value)
6 RAISE_VARARGS 2
9 LOAD_CONST 0 (None)
12 RETURN_VALUE dis.dis(compile('raise SomeError(value)','','exec')) 1 0 LOAD_NAME 0 (SomeError)
3 LOAD_NAME 1 (value)
6 CALL_FUNCTION 1
9 RAISE_VARARGS 1
12 LOAD_CONST 0 (None)
15 RETURN_VALUE

I guess that comes from the grammar of the raise statement, i.e.,

raise_stmt: 'raise' [test [',' test [',' test]]]

which allows up to three arguments for raise, apparently all general
expressions, but with some specific run-time requirements for what
combinations of argument expression values are allowable.

I.e., it seems (I haven't looked in ceval.c(?)) that RAISE_VARARGS must
look at the first item in its count of args on the stack, and in the
SomeError, value case find an exception class, and decide to instantiate it
and throw the instance, but if it finds the instance ready made, as in
SomeError(value), it must skip the instantiation (and disallow further args BTW).
raise Exception, 'arg' Traceback (most recent call last):
File "<stdin>", line 1, in ?
Exception: arg raise Exception('arg') Traceback (most recent call last):
File "<stdin>", line 1, in ?
Exception: arg

Those looks the same, but sensitivity to the type of the first arg is revealed by
raise Exception('arg'), 'what now?' Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: instance exception may not have a separate value
raise Exception('arg', 'what now?') Traceback (most recent call last):
File "<stdin>", line 1, in ?
Exception: ('arg', 'what now?')


(that is, create a SomeError exception and pass the value as its
first argument).

you can use either form in your code (I prefer the latter myself).

Just to drive home the general expression allowability in raise,
and instance vs class as the first arg:
extab = [StopIteration('stop stop ;-)'), ValueError('wrong value')] raise extab[1] Traceback (most recent call last):
File "<stdin>", line 1, in ?
ValueError: wrong value
raise extab[0] Traceback (most recent call last):
File "<stdin>", line 1, in ?
StopIteration: stop stop ;-)
extab = [StopIteration, 'stop stop ;-)', ValueError, 'wrong value'] raise extab[2], extab[3] Traceback (most recent call last):
File "<stdin>", line 1, in ?
ValueError: wrong value
raise extab[0], extab[1]

Traceback (most recent call last):
File "<stdin>", line 1, in ?
StopIteration: stop stop ;-)

Ok, I'll stop ;-)

Regards,
Bengt Richter
Nov 22 '05 #45

P: n/a
On Wed, 16 Nov 2005 17:39:57 +0100, "Fredrik Lundh" <fr*****@pythonware.com> wrote:
Rick Wotnaz wrote.
... which leads me to belive that 'msg' is not type(str). It can be
coerced (str(msg).find works as expected). But what exactly is msg?
It appears to be of <type 'instance'>, and does not test equal to a
string.
it's an instance of the exception type, of course.

:::

if you do

raise SomeError, value

Python will actually do

raise SomeError(value)


Depending on what you mean by "actually" I guess ...
(I'm sure you know this and more ;-)
dis.dis(compile('raise SomeError, value','','exec')) 1 0 LOAD_NAME 0 (SomeError)
3 LOAD_NAME 1 (value)
6 RAISE_VARARGS 2
9 LOAD_CONST 0 (None)
12 RETURN_VALUE dis.dis(compile('raise SomeError(value)','','exec')) 1 0 LOAD_NAME 0 (SomeError)
3 LOAD_NAME 1 (value)
6 CALL_FUNCTION 1
9 RAISE_VARARGS 1
12 LOAD_CONST 0 (None)
15 RETURN_VALUE

I guess that comes from the grammar of the raise statement, i.e.,

raise_stmt: 'raise' [test [',' test [',' test]]]

which allows up to three arguments for raise, apparently all general
expressions, but with some specific run-time requirements for what
combinations of argument expression values are allowable.

I.e., it seems (I haven't looked in ceval.c(?)) that RAISE_VARARGS must
look at the first item in its count of args on the stack, and in the
SomeError, value case find an exception class, and decide to instantiate it
and throw the instance, but if it finds the instance ready made, as in
SomeError(value), it must skip the instantiation (and disallow further args BTW).
raise Exception, 'arg' Traceback (most recent call last):
File "<stdin>", line 1, in ?
Exception: arg raise Exception('arg') Traceback (most recent call last):
File "<stdin>", line 1, in ?
Exception: arg

Those looks the same, but sensitivity to the type of the first arg is revealed by
raise Exception('arg'), 'what now?' Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: instance exception may not have a separate value
raise Exception('arg', 'what now?') Traceback (most recent call last):
File "<stdin>", line 1, in ?
Exception: ('arg', 'what now?')


(that is, create a SomeError exception and pass the value as its
first argument).

you can use either form in your code (I prefer the latter myself).

Just to drive home the general expression allowability in raise,
and instance vs class as the first arg:
extab = [StopIteration('stop stop ;-)'), ValueError('wrong value')] raise extab[1] Traceback (most recent call last):
File "<stdin>", line 1, in ?
ValueError: wrong value
raise extab[0] Traceback (most recent call last):
File "<stdin>", line 1, in ?
StopIteration: stop stop ;-)
extab = [StopIteration, 'stop stop ;-)', ValueError, 'wrong value'] raise extab[2], extab[3] Traceback (most recent call last):
File "<stdin>", line 1, in ?
ValueError: wrong value
raise extab[0], extab[1]

Traceback (most recent call last):
File "<stdin>", line 1, in ?
StopIteration: stop stop ;-)

Ok, I'll stop ;-)

Regards,
Bengt Richter
Nov 22 '05 #46

P: n/a
Steven D'Aprano <st***@REMOVETHIScyber.com.au> wrote:
# Create an instance of the exception you expect:
try:
for i in 0:
pass
except TypeError, ITER_OVER_NON_SEQ:
pass
# Now run your code...
try:
...blah blah blah...
except TypeError, msg
if str(msg) == str(ITER_OVER_NON_SEQ):
...blah blah blah...

This means we're no longer assuming what the error message will be,
which makes our code a lot more future-proof and implementation-proof: if
some version of Python changes the error string from "iteration over
non-sequence" to something else, the code should continue to work
correctly.


Now you're assuming that the message is a constant for all TypeErrors
caused by attempting to iterate over a non-iterable object. I don't
see anything in the docs which prevents

try:
for i in 0:
pass
except TypeError, ex:
print ex

from printing "Cannot iterate over integer with value 0", or even
"Cannot iteratate over object at address <0x456778>". Let's not even
talk about some future implementor who decides that the strings should
contain timestamps. None of these things are forbidden by the spec,
therefore you should assume they will all happen.

The best solution I've seen suggested so far is to coerce the
passed-in object to an iter and see what happens:

try:
thingieIterator = iter (thingie)
except TypeError:
raise NonIterableThingieError

Once you've done that, I think you can either do:

for item in thingie:

or

for item in thingieIterator:

with equal effect. And I'm reasonably sure this will work fine with
non-idempotent thingies.
Nov 22 '05 #47

P: n/a
Steven D'Aprano <st***@REMOVETHIScyber.com.au> wrote:
# Create an instance of the exception you expect:
try:
for i in 0:
pass
except TypeError, ITER_OVER_NON_SEQ:
pass
# Now run your code...
try:
...blah blah blah...
except TypeError, msg
if str(msg) == str(ITER_OVER_NON_SEQ):
...blah blah blah...

This means we're no longer assuming what the error message will be,
which makes our code a lot more future-proof and implementation-proof: if
some version of Python changes the error string from "iteration over
non-sequence" to something else, the code should continue to work
correctly.


Now you're assuming that the message is a constant for all TypeErrors
caused by attempting to iterate over a non-iterable object. I don't
see anything in the docs which prevents

try:
for i in 0:
pass
except TypeError, ex:
print ex

from printing "Cannot iterate over integer with value 0", or even
"Cannot iteratate over object at address <0x456778>". Let's not even
talk about some future implementor who decides that the strings should
contain timestamps. None of these things are forbidden by the spec,
therefore you should assume they will all happen.

The best solution I've seen suggested so far is to coerce the
passed-in object to an iter and see what happens:

try:
thingieIterator = iter (thingie)
except TypeError:
raise NonIterableThingieError

Once you've done that, I think you can either do:

for item in thingie:

or

for item in thingieIterator:

with equal effect. And I'm reasonably sure this will work fine with
non-idempotent thingies.
Nov 22 '05 #48

P: n/a
Fredrik Lundh wrote:
Steven D'Aprano wrote:

This means we're no longer assuming what the error message will be,
which makes our code a lot more future-proof and implementation-proof: if
some version of Python changes the error string from "iteration over
non-sequence" to something else, the code should continue to work
correctly.

Alex has already posted the right way to do this.


Has he? Where and when? Is my crystal ball broken
again? I hate it when I miss the one Usenet post all
the cool kids are talking about.

can you please stop


Can I please stop what? Using punctuation and
capitalization correctly? Learning? Thinking for
myself? Posting to Usenet? Programming in Python? Your
post is unclear.

--
Steven.

Nov 22 '05 #49

P: n/a
Fredrik Lundh wrote:
Steven D'Aprano wrote:

This means we're no longer assuming what the error message will be,
which makes our code a lot more future-proof and implementation-proof: if
some version of Python changes the error string from "iteration over
non-sequence" to something else, the code should continue to work
correctly.

Alex has already posted the right way to do this.


Has he? Where and when? Is my crystal ball broken
again? I hate it when I miss the one Usenet post all
the cool kids are talking about.

can you please stop


Can I please stop what? Using punctuation and
capitalization correctly? Learning? Thinking for
myself? Posting to Usenet? Programming in Python? Your
post is unclear.

--
Steven.

Nov 22 '05 #50

70 Replies

This discussion thread is closed

Replies have been disabled for this discussion.