Raise X or Raise X()?
steve+comp.lang.python at pearwood.info
Mon Mar 12 16:08:49 CET 2012
On Mon, 12 Mar 2012 14:52:49 +0100, Stefan Behnel wrote:
>> "raise X" is a special case of the 3-args raise. Effectively it just
>> raises an instance of X which is constructed with an empty argument
>> list. Therefore, "raise X()" is equivalent, as far as I know.
> Not completely, although that may be considered an implementation
> When you raise an exception instance, it gets instantiated before being
> raised (by your own code). So you control exactly how the exception
> instance comes to life.
Normally the exception is instantiated just before you raise it. Of
course, that's not compulsory. You can pre-load it if you want:
instance = ValueError("spam")
but generally we say
and be done with it.
> When you raise the class instead of the instance, it may or may not get
> instantiated, depending on how it is being caught or discarded (and by
> whom, e.g. internally in the interpreter). In most cases, it will be
> instantiated, but only when someone catches it, i.e. at a later time,
> after raising it.
I don't think that is correct. Either the exception gets raised, or it
doesn't. If it doesn't get raised, then no instance is instantiated
(unless you did it yourself, as in the example above). But if it does get
raised, then regardless of which form you use (the class or the
instance), the exception instance *will* be instantiated. If you don't do
it yourself, the raise statement will do it.
Using Python 3.2:
>>> class TestException(Exception):
... def __init__(self, *args):
... print("initialising exception")
... raise TestException
Whether you catch the exception or not, it still gets instantiated. Try
it and see, and if you can find some way to actually raise the exception
without instantiating the instance, I would love to see it.
Implementation-wise, at least for Python 3.2, what seems to happen as
best as I can tell from reading ceval.c, is that the opcode for raise
checks the argument. If it is already an instance, it uses that; if it is
not, it instantiates it immediately.
(see function do_raise in ceval.c)
So, technically, there may be a minuscule timing difference depending on
whether you instantiate the exception in Python code or in C code, but I
don't believe that this meaningful.
> If the instantiation of the exception has side
> effects, or if it fails for some reason (e.g. bad or missing arguments),
> the time of instantiation may make a difference.
I expect this is only relevant if you pre-instantiate the exception ahead
I suppose it is conceivable that, in a particularly odd corner case or
two (perhaps using exceptions with side effects and/or threads or
something) the nanosecond difference between raise X() and raise X might
make a difference. But I'm having difficulty seeing that this is
plausible. I think you will have to show me an example to prove it.
Certainly, without threads, I don't think there is any difference.
> It's obviously bad
> design to use side effects here, but it's equally bad design to silently
> rely on it being side effect free.
I don't understand what you are trying to say here. We rely on code being
side-effect free *all the time*. Or at least known side-effects, such as
import or list.append.
> Note that if the exception happens to never get instantiated, you will
> safe a tiny bit of time for the overall propagation.
I don't believe that is true. Looking at the C code, the exception
appears to always be instantiated once you call raise.
> But since it's hard
> to tell if that will happen or not, it would be a rather misguided micro
> optimisation for most Python code to base your decision on this, at
> least without prior benchmarking (with a real work-load etc.).
> In general, I tend to raise exception types only for very safe and
> common built-in exceptions, such as StopIteration, but whenever they
> take an argument, I raise the instance instead. For user provided
> exceptions, there is no real excuse for raising the type.
My excuses are:
* sometimes I don't need an error message, so why give one?
* and it makes no difference whether I instantiate the exception or let
raise do it for me
> BTW, StopIteration takes an optional argument in Python 3.3,
The time machine strikes again. StopIteration takes an optional argument
going back to at least 2.6.
steve at runes:~$ python2.6
Python 2.6.6 (r266:84292, Dec 27 2010, 00:02:40)
[GCC 4.4.5] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> raise StopIteration("out of fuel")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration: out of fuel
More information about the Python-list