Python Mystery Theatre -- Episode 1: Exceptions

Erik Max Francis max at alcyone.com
Sat Jul 12 10:02:16 CEST 2003


Raymond Hettinger wrote:

(I've pasted in code I needed to use to illustrate or understand the
problems.)

> ACT I ---------------------------------------
> >>> s = list('abc')
> >>> try:
> ...     result = s['a']
> ... except IndexError, TypeError:
> ...     print 'Not found'
> ...
> 
> Traceback (most recent call last):
>   File "<pyshell#11>", line 2, in -toplevel-
>     result = s['a']
> TypeError: list indices must be integers

This one's easy.  You didn't try to catch IndexErrors and TypeErrors,
you tried to catch an IndexError and, if caught, assign the exception
instance to TypeError.  The except clause takes an exception class or
tuple of exception classes, and then an optional instance variable name,
and then some other optional things.  So:

	...
	except (IndexError, TypeError):
	...

will fix this.

 > ACT II --------------------------------------------
> >>> class MyMistake(Exception):
> ...     pass
> 
> >>> try:
> ...     raise MyMistake, 'try, try again'
> ... except MyMistake, msg:
> ...     print type(msg)
> ...
> 
> <type 'instance'>

I'm not sure what mystery you're trying to get at here.  This is what
Python prints for all instances:

>>> class C:
...  pass
... 
>>> c = C()
>>> type(c)
<type 'instance'>

An exception is an instance of an exception class, so it looks like an
instance like any other.  If you wanted the name of the class, then use
__class__:

>>> c.__class__
<class __main__.C at 0x814f9e4>
>>> c.__class__.__name__
'C'

> ACT III --------------------------------------------
> >>> class Prohibited(Exception):
> ...     def __init__(self):
> ...         print 'This class of should never get initialized'
> ...
> >>> raise Prohibited()
> This class of should never get initialized
> 
> Traceback (most recent call last):
>   File "<pyshell#40>", line 1, in -toplevel-
>     raise Prohibited()
> Prohibited: <unprintable instance object>
> >>> raise Prohibited
> This class of should never get initialized
> 
> Traceback (most recent call last):
>   File "<pyshell#41>", line 1, in -toplevel-
>     raise Prohibited
> Prohibited: <unprintable instance object>

Is this some IDLE-specific thing?  I don't see this at all:

>>> class Prohibited(Exception):
...  def __init__(self):
...   print 'This class should have never gotten initialized'
... 
>>> raise Prohibited()
This class should have never gotten initialized
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
__main__.Prohibited>>> 
>>> raise Prohibited
This class should have never gotten initialized
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
__main__.Prohibited>>> 

There is indeed no newline between the printed names of the class and
the following prompt, this is not a pasting error, which strongly
suggests to me that it's what you're trying to get at but is exhibiting
itself in a different way in the interactive interpreter vs. IDLE. 
Undoubtedly it happens because Prohibited overrides Exception, and
Prohibited needs an __init__ method, but that method does not call
Exception.__init__.  At the very least, fixing this for me makes the
output a little more sane:

>>> class Proscribed(Exception):
...  def __init__(self):
...   Exception.__init__(self)
...   print "That information is proscribed. Enter your DNA#." 
... 
>>> raise Proscribed()
That information is proscribed. Enter your DNA#.
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
__main__.Proscribed
>>> raise Proscribed  
That information is proscribed. Enter your DNA#.
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
__main__.Proscribed

> ACT IV -----------------------------------------------
> >>> module = 'Root'
> >>> try:
> ...     raise module + 'Error'
> ... except 'LeafError':
> ...     print 'Need leaves'
> ... except 'RootError':
> ...     print 'Need soil'
> ... except:
> ...     print 'Not sure what is needed'
> ...
> 
> Not sure what is needed

You used string exceptions and so deserve punishment :-).  In this case,
string exceptions in except clauses are tested by identity, not
equality, so building them with concatenation is unlikely to create a
string with the same ID:

>>> s = "dataspace retrieval"
>>> t = "dataspace " + "retrieval"
>>> s is t
0
>>> s == t
1

Equality and identity aren't the same thing, and this is one of the rare
cases in Python where identity really matters (usually, it's merely an
optimization implementation detail).  Short answer:  Don't use string
exceptions.  Long answer:  Seriously, don't use string exceptions.

> ACT V -----------------------------------------------
> >>> try:
> ...     raise KeyError('Cannot find key')
> ... except LookupError, msg:
> ...     print 'Lookup:', msg
> ... except OverflowError, msg:
> ...     print 'Overflow:', msg
> ... except KeyError, msg:
> ...     print 'Key:', msg
> 
> Lookup: 'Cannot find key'

This means that KeyError is a subclass of LookupError (something I
wouldn't have known off the top of my head but which was easy to
verify):

>>> issubclass(KeyError, LookupError)
1

Except clauses go in order and test to see whether the exception object
is an instance of the specified class or any of its subclasses.  Since
KeyError is a subclass of LookupError, all KeyErrors are LookupErrors
too, and that's the except clause that gets executed.  If you actually
_did_ want to distinguish between KeyErrors and LookupErrors, you can
put the KeyError clause first:

	...
	except KeyError, e:
	    ...
	except LookupError, e:
	    ...
	...

-- 
   Erik Max Francis && max at alcyone.com && http://www.alcyone.com/max/
 __ San Jose, CA, USA && 37 20 N 121 53 W && &tSftDotIotE
/  \ God said:  "Let Newton be"; and all was light.
\__/  Alexander Pope




More information about the Python-list mailing list