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 > detail. > > 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") time.sleep(60) raise instance but generally we say raise ValueError("spam") 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") ... super().__init__(*args) ... >>> try: ... raise TestException ... except: ... pass ... initialising exception 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 of time. 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@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 -- Steven -- http://mail.python.org/mailman/listinfo/python-list