Today when I am explaining that in Python 3,
there are two ways to raise exceptions:
raise Exception
raise Exception()
and that the first one is the same
as the second one, as Python will add the
missing pair of parenthesis.
I felt their pain as they gasped.
Before that, I have already explained
to them this piece of code:
try: raise SomeException()
except SomeException:
print('Got an exception here')
by saying that the except-clause
will match anything
that belong to the SomeException class.
Without knowing this secrete
piece of information (that a
pair of parenthesis is automatically
provided), the following code
would be hard to understand:
try: raise SomeException
except SomeException:
print('Got an exception here')
because the class object SomeException
is not an instance of itself, so
a not-so-crooked coder will not
consider a match here.
So, the explicit is better than
implicit rule is thrown out of
the window so cheaply,
that it literally worth less
than an empty tuple!
Regards,
Yingjie
> Today when I am explaining that in Python 3,
> there are two ways to raise exceptions:
>
> raise Exception
>
> raise Exception()
>
> and that the first one is the same
> as the second one, as Python will add the
> missing pair of parenthesis.
>
In fact this is noting to do with Python 3 - the same is true of Python
2, so this isn't new:
Python 2.6.5 (r265:79063, Jun 12 2010, 17:07:01)
[GCC 4.3.4 20090804 (release) 1] on cygwin
Type "help", "copyright", "credits" or "license" for more information.
>>> raise KeyError
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError
>>> raise KeyError("Here is the message")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'Here is the message'
>>>
> I felt their pain as they gasped.
> Before that, I have already explained
> to them this piece of code:
>
> try: raise SomeException()
> except SomeException:
> print('Got an exception here')
>
> by saying that the except-clause
> will match anything
> that belong to the SomeException class.
>
Or any of its subclasses ...
> Without knowing this secrete
> piece of information (that a
> pair of parenthesis is automatically
> provided), the following code
> would be hard to understand:
>
> try: raise SomeException
> except SomeException:
> print('Got an exception here')
>
> because the class object SomeException
> is not an instance of itself, so
> a not-so-crooked coder will not
> consider a match here.
>
It's a matter of understanding correctly how the interpreter operates
(and the interactive interpreter session is the ideal place to
investigate this). The 2.7 documentation for the raise statement says
(and this is not new):
"""
If the first object is an instance, the type of the exception is the
class of the instance, the instance itself is the value, and the second
object must be None.
If the first object is a class, it becomes the type of the exception.
The second object is used to determine the exception value: If it is an
instance of the class, the instance becomes the exception value. If the
second object is a tuple, it is used as the argument list for the class
constructor; if it is None, an empty argument list is used, and any
other object is treated as a single argument to the constructor. The
instance so created by calling the constructor is used as the exception
value.
"""
So the interpreter doesn't really "automatically provide a pair of
parentheses", but examines the exception object and instantiates it if
it is a class.
> So, the explicit is better than
> implicit rule is thrown out of
> the window so cheaply,
> that it literally worth less
> than an empty tuple!
>
Surely an exaggeration. In fact current best practice (which you should
inform yourself of as best you can to help you in your teaching work -
so you are to be congratulated for bringing this question to the list)
is to always use explicit calls, with arguments specifying a tailored
message.
> Regards,
>
> Yingjie
>
>
regards
Steve
--
Steve Holden +1 571 484 6266 +1 800 494 3119
PyCon 2011 Atlanta March 9-17 http://us.pycon.org/
See Python Video! http://python.mirocommunity.org/
Holden Web LLC http://www.holdenweb.com/
A very cogent message -- the end echos the start. :)
I must say that I learned from you a new angle to
think about this issue. On the other hand, I still
feel that when allowing both ways colliding into
the simpleness and bueaty of the language, we
should consider to make a decision.
Sure, this introduced quite a lot of complexity
when the doc has to give a very long explanation of
what is happening in order to justify it.
As I am thinking about it, it seems two
conflicting intuition of code comprehension
are at work here:
Intuition #1: as if you raise an exception
type, and then match that type.
It seems that no instances
are involved here (Intuitively).
See an example code here:
try: raise KeyError
except KeyError: pass
Intuition #2: you raise an exception
instance, and then match an instance by
its type. See an example code here:
try: raise KeyError()
except KeyError as ke: pass
Those two comprehensions are not compatible,
and thus the one that promotes correct
understanding should be encouraged,
while the other should be discouraged,
and maybe even be made iliegal.
Regards,
Yingjie
I prefer to treat those two cases as unified, by observing that if what
is raised in an exception class than an instance is created by calling
it with no arguments. So matching is always by the instance's type -
it's just that the instance creation can be implicit. I agree with you
that explicit is better.
Most of the syntactic variation you dislike is to allow existing code to
continue to work. Some of it is removed in Python 3, when backwards
compatibility could be ignored.
I agree with you that this is annoying. I think it is a holdover from
the past. In Python 1/2, raise 'some string' also works, but that was
disallowed in Py 3. A lot of things were cleaned up in Py 3, but not
everything.
--
Terry Jan Reedy
> Intuition #1: as if you raise an exception type, and then match that
> type.
> It seems that no instances
> are involved here (Intuitively).
Your intuition is not my intuition, nor does it match what Python
actually does. You can only go so far on *guessing* what a programming
statement does, sometimes you need to actually read the Fine Manual.
> See an example code here:
>
> try: raise KeyError
> except KeyError: pass
As the documentation states, this creates an instance of KeyError.
raise evaluates the first expression as the exception object.
It must be either a subclass or an instance of BaseException.
If it is a class, the exception instance will be obtained when
needed by instantiating the class with no arguments.
http://docs.python.org/py3k/reference/simple_stmts.html#the-raise-statement
So there is no semantic difference between "raise KeyError" and
"raise KeyError()". Why should there be? What practical difference would
you expect?
If you do this:
try: raise KeyError
except KeyError as ke: print(ke)
you will see that the value caught is an instance, not the class.
> Intuition #2: you raise an exception
> instance, and then match an instance by its type. See an example code
> here:
>
> try: raise KeyError()
> except KeyError as ke: pass
Your intuition is wrong. Exceptions aren't matched by type, they are
*always* matched by an isinstance() check, and that includes subclasses.
>>> try: raise KeyError # with or without parentheses makes no difference
... except Exception as e: print(type(e))
...
<class 'KeyError'>
> Those two comprehensions are not compatible, and thus the one that
> promotes correct understanding should be encouraged,
> while the other should be discouraged, and maybe even be made iliegal.
You seem to have misunderstood both forms of the raise statement. Should
we make exceptions illegal because you can't correctly guess what they do?
--
Steven
Though what you said about Python is right,
I think somehow you missed my point a bit.
Ideally, the language could be so 'natural'
that it means what meets the eyes.
Yingjie
There *can* be a difference though.
>>> raise UnicodeDecodeError
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: function takes exactly 5 arguments (0 given)
:-)
>
> You seem to have misunderstood both forms of the raise statement. Should
> we make exceptions illegal because you can't correctly guess what they do?
Sometimes people not being able to understand them is a good reason
for making things illegal (or rather for not making them legal in the
first place). I don't think it applies to this particular case though.
All the best,
Michael Foord