Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.python > #21501 > unrolled thread

Raise X or Raise X()?

Started bybvdp <bob@mellowood.ca>
First post2012-03-11 12:04 -0700
Last post2012-03-13 00:13 +1100
Articles 13 — 10 participants

Back to article view | Back to comp.lang.python


Contents

  Raise X or Raise X()? bvdp <bob@mellowood.ca> - 2012-03-11 12:04 -0700
    Re: Raise X or Raise X()? Irmen de Jong <irmen.NOSPAM@xs4all.nl> - 2012-03-11 21:37 +0100
      Re: Raise X or Raise X()? Chris Rebert <clp2@rebertia.com> - 2012-03-11 14:49 -0700
      Re: Raise X or Raise X()? Stefan Behnel <stefan_ml@behnel.de> - 2012-03-12 14:52 +0100
        Re: Raise X or Raise X()? Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2012-03-12 15:08 +0000
          Re: Raise X or Raise X()? Stefan Behnel <stefan_ml@behnel.de> - 2012-03-12 17:08 +0100
    Re: Raise X or Raise X()? Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2012-03-11 23:59 +0000
      Re: Raise X or Raise X()? bvdp <bob@mellowood.ca> - 2012-03-11 18:53 -0700
      Re: Raise X or Raise X()? MRAB <python@mrabarnett.plus.com> - 2012-03-12 02:26 +0000
    Re: Raise X or Raise X()? Jean-Michel Pichavant <jeanmichel@sequans.com> - 2012-03-12 11:37 +0100
    Re: Raise X or Raise X()? Robert Kern <robert.kern@gmail.com> - 2012-03-12 12:47 +0000
    Re: Raise X or Raise X()? James Elford <fil.oracle@gmail.com> - 2012-03-12 13:06 +0000
    Re: Raise X or Raise X()? Chris Angelico <rosuav@gmail.com> - 2012-03-13 00:13 +1100

#21501 — Raise X or Raise X()?

Frombvdp <bob@mellowood.ca>
Date2012-03-11 12:04 -0700
SubjectRaise X or Raise X()?
Message-ID<387014.2537.1331492695725.JavaMail.geo-discussion-forums@pbjv6>
Which is preferred in a raise: X or X()? I've seen both. In my specific case I'm dumping out of a deep loop:

try:
  for ...
    for ...
      for ...
        if match:
           raise StopInteration()
         else ...

except StopInteration:
   print "found it"

[toc] | [next] | [standalone]


#21504

FromIrmen de Jong <irmen.NOSPAM@xs4all.nl>
Date2012-03-11 21:37 +0100
Message-ID<4f5d0d06$0$6856$e4fe514c@news2.news.xs4all.nl>
In reply to#21501
On 11-3-2012 20:04, bvdp wrote:
> Which is preferred in a raise: X or X()? I've seen both. In my specific case I'm dumping out of a deep loop:
> 
> try:
>   for ...
>     for ...
>       for ...
>         if match:
>            raise StopInteration()
>          else ...
> 
> except StopInteration:
>    print "found it"

"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.

See http://docs.python.org/reference/simple_stmts.html#the-raise-statement

Irmen

[toc] | [prev] | [next] | [standalone]


#21507

FromChris Rebert <clp2@rebertia.com>
Date2012-03-11 14:49 -0700
Message-ID<mailman.575.1331502573.3037.python-list@python.org>
In reply to#21504
On Sun, Mar 11, 2012 at 1:37 PM, Irmen de Jong <irmen.NOSPAM@xs4all.nl> wrote:
> On 11-3-2012 20:04, bvdp wrote:
>> Which is preferred in a raise: X or X()? I've seen both. In my specific case I'm dumping out of a deep loop:
>>
>> try:
>>   for ...
>>     for ...
>>       for ...
>>         if match:
>>            raise StopInteration()
>>          else ...
>>
>> except StopInteration:
>>    print "found it"
>
> "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.
>
> See http://docs.python.org/reference/simple_stmts.html#the-raise-statement

Note that the 3-argument form of `raise` has been eliminated in Python 3.
However, both:
    raise an_exception_instance
and:
    raise AnExceptionClass # will have a blank error message
are still permitted.

Interesting stylistic question though. I'd support the X() form for
uniformity with the majority of cases where an error message is
specified for the exception.

Cheers,
Chris

[toc] | [prev] | [next] | [standalone]


#21535

FromStefan Behnel <stefan_ml@behnel.de>
Date2012-03-12 14:52 +0100
Message-ID<mailman.587.1331560385.3037.python-list@python.org>
In reply to#21504
Irmen de Jong, 11.03.2012 21:37:
> On 11-3-2012 20:04, bvdp wrote:
>> Which is preferred in a raise: X or X()? I've seen both. In my specific case I'm dumping out of a deep loop:
>>
>> try:
>>   for ...
>>     for ...
>>       for ...
>>         if match:
>>            raise StopInteration()
>>          else ...
>>
>> except StopInteration:
>>    print "found it"
> 
> "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.

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. 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. 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.

Note that if the exception happens to never get instantiated, you will safe
a tiny bit of time for the overall propagation. 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.

BTW, StopIteration takes an optional argument in Python 3.3, but that's not
a feature that is generally going to be used by Python code, I assume. So
I'll just keep raising the type. :)

Stefan

[toc] | [prev] | [next] | [standalone]


#21536

FromSteven D'Aprano <steve+comp.lang.python@pearwood.info>
Date2012-03-12 15:08 +0000
Message-ID<4f5e1180$0$29891$c3e8da3$5496439d@news.astraweb.com>
In reply to#21535
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

[toc] | [prev] | [next] | [standalone]


#21538

FromStefan Behnel <stefan_ml@behnel.de>
Date2012-03-12 17:08 +0100
Message-ID<mailman.588.1331568502.3037.python-list@python.org>
In reply to#21536
Steven D'Aprano, 12.03.2012 16:08:
> 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.
>
>> 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:

Note that the exception handling was seriously reworked in Python 3, so
there are substantial differences to Python 2. Although maybe not so many
at the Python code level.


> >>> 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)

Right, it normalises the exception immediately, also in older Python
versions. That's different at the C layer, but it looks like the
interpreter does the right (i.e. only safe) thing for Python code. Good to
know - and thanks for clearing this up.


> 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.

The difference can be substantial in C code, especially for something like
StopIteration if the instantiation can be avoided (in some cases even
raising the exception is avoided completely!).

However, given that Python code apparently can't raise types without
instantiating them, I agree that it's not worth looking for a difference at
that level.


> 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.

Even with threads it will be hard to get at this difference due to the GIL.
I can't see anything in do_raise() that would allow foreign code to run
between the evaluation of the "raise" opcode and starting to instantiate
the exception.


>> 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*.

That's fine for builtin stuff, functions etc. But for user provided
exceptions, why should you make your code dependent on a side effect free
instantiation just to be able to raise them without parentheses? Sounds
like too little a gain to me.

Besides, this is getting way hypothetical.


>> 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.

My fault, I was thinking at the C level again.


>> 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

Right, I think it's even at least 2.4 and likely much older than that. What
I meant to say was that StopIteration has a "value" (an actual property) in
Python 3.3, which represents its first argument. So that argument actually
has specific semantics (see PEP 380), instead of just "being there".

Stefan

[toc] | [prev] | [next] | [standalone]


#21513

FromSteven D'Aprano <steve+comp.lang.python@pearwood.info>
Date2012-03-11 23:59 +0000
Message-ID<4f5d3c4c$0$29891$c3e8da3$5496439d@news.astraweb.com>
In reply to#21501
On Sun, 11 Mar 2012 12:04:55 -0700, bvdp wrote:

> Which is preferred in a raise: X or X()? 

Both.

Always use raise "X(*args)" when you need to provide arguments (which you 
should always do for exceptions meant for the caller to see). The form 
"raise X, args" should be considered discouraged, and in fact is gone in 
Python 3.x.

Purely internal exceptions (which you raise and catch yourself) don't 
need arguments, so there is no difference between the two forms: 
"raise X" is exactly equivalent to "raise X()" with no arguments. Use 
whichever takes your fancy.

Personally, I used "raise X" to mean "this doesn't need arguments and 
should never have any" and "raise X()" to mean "this needs arguments but 
I'm too lazy to provide them right now". Think of it as a FIXME.


-- 
Steven

[toc] | [prev] | [next] | [standalone]


#21517

Frombvdp <bob@mellowood.ca>
Date2012-03-11 18:53 -0700
Message-ID<12235670.2782.1331517186309.JavaMail.geo-discussion-forums@pbjv6>
In reply to#21513
Thanks all for the comments.

> Personally, I used "raise X" to mean "this doesn't need arguments and 
> should never have any" and "raise X()" to mean "this needs arguments but 
> I'm too lazy to provide them right now". Think of it as a FIXME.

Yes, that makes as much sense as anything else :)

[toc] | [prev] | [next] | [standalone]


#21518

FromMRAB <python@mrabarnett.plus.com>
Date2012-03-12 02:26 +0000
Message-ID<mailman.579.1331519202.3037.python-list@python.org>
In reply to#21513
On 11/03/2012 23:59, Steven D'Aprano wrote:
> On Sun, 11 Mar 2012 12:04:55 -0700, bvdp wrote:
>
>>  Which is preferred in a raise: X or X()?
>
> Both.
>
> Always use raise "X(*args)" when you need to provide arguments (which you
> should always do for exceptions meant for the caller to see). The form
> "raise X, args" should be considered discouraged, and in fact is gone in
> Python 3.x.
>
> Purely internal exceptions (which you raise and catch yourself) don't
> need arguments, so there is no difference between the two forms:
> "raise X" is exactly equivalent to "raise X()" with no arguments. Use
> whichever takes your fancy.
>
> Personally, I used "raise X" to mean "this doesn't need arguments and
> should never have any" and "raise X()" to mean "this needs arguments but
> I'm too lazy to provide them right now". Think of it as a FIXME.
>
As some do need arguments, I'd do the opposite; no parentheses would
mean "I haven't finished yet". :-)

[toc] | [prev] | [next] | [standalone]


#21527

FromJean-Michel Pichavant <jeanmichel@sequans.com>
Date2012-03-12 11:37 +0100
Message-ID<mailman.583.1331548677.3037.python-list@python.org>
In reply to#21501
bvdp wrote:
> Which is preferred in a raise: X or X()? I've seen both. In my specific case I'm dumping out of a deep loop:
>
> try:
>   for ...
>     for ...
>       for ...
>         if match:
>            raise StopInteration()
>          else ...
>
> except StopInteration:
>    print "found it"
>   

I prefer the raise X() version, it fulfils the zen of python :

"Special cases aren't special enough to break the rules.
There should be one-- and preferably only one --obvious way to do it."

I still wonder why they've added the class raise form, on which purpose.

JM

[toc] | [prev] | [next] | [standalone]


#21530

FromRobert Kern <robert.kern@gmail.com>
Date2012-03-12 12:47 +0000
Message-ID<mailman.584.1331556465.3037.python-list@python.org>
In reply to#21501
On 3/12/12 10:37 AM, Jean-Michel Pichavant wrote:
> bvdp wrote:
>> Which is preferred in a raise: X or X()? I've seen both. In my specific case
>> I'm dumping out of a deep loop:
>>
>> try:
>> for ...
>> for ...
>> for ...
>> if match:
>> raise StopInteration()
>> else ...
>>
>> except StopInteration:
>> print "found it"
>
> I prefer the raise X() version, it fulfils the zen of python :
>
> "Special cases aren't special enough to break the rules.
> There should be one-- and preferably only one --obvious way to do it."
>
> I still wonder why they've added the class raise form, on which purpose.

The class raise form used to be the only way to raise exceptions. To pass an 
argument, there was special syntax:

   raise Exception, "some message"

This syntax has been done away with in Python 3 in favor of regular calling 
conventions. Python 3 still allows bare classes, though. I also prefer to always 
raise instances of exceptions rather than bare exception classes. It simplifies 
the mental model.

-- 
Robert Kern

"I have come to believe that the whole world is an enigma, a harmless enigma
  that is made terrible by our own mad attempt to interpret it as though it had
  an underlying truth."
   -- Umberto Eco

[toc] | [prev] | [next] | [standalone]


#21532

FromJames Elford <fil.oracle@gmail.com>
Date2012-03-12 13:06 +0000
Message-ID<mailman.585.1331557597.3037.python-list@python.org>
In reply to#21501
On 11/03/12 19:04, bvdp wrote:
> Which is preferred in a raise: X or X()? I've seen both. In my specific case I'm dumping out of a deep loop:
> 
> try:
>   for ...
>     for ...
>       for ...
>         if match:
>            raise StopInteration()
>          else ...
> 
> except StopInteration:
>    print "found it"

I wonder whether you need to use an exception here rather than a yield
statement? Exceptions should reflect Exceptional circumstances (and come
with associated stack trace, and so on...). The following should do
something like what you want, without raising exceptions.

>>> # Deeply loop into a collection of collections
>>> def find(collection):
...    for sub_col in collection:
...        for item in sub_col:
...            for foo in item.list_field:
...                if foo.is_match:
...                    yield foo

>>> # Some junk classes to iterate over
>>> class Item(object):
...    def __init__(self, some_range):
...        self.list_field = [ListedItem(i) for i in some_range]

>>> class ListedItem(object):
...    def __init__(self, number):
...        self.tag = number
...        self.is_match = False

>>>    def __str__(self):
...        return str(self.tag)

>>> # Construct a list of items
>>> l = [[Item(range(i)) for i in range(10)],
...	[Item(range(i, 2*i)) for i in range(10,20)]]

>>> l[0][9].list_field[3].is_match = True

>>> for i in find(l):
...     print(i)
3

James

[toc] | [prev] | [next] | [standalone]


#21533

FromChris Angelico <rosuav@gmail.com>
Date2012-03-13 00:13 +1100
Message-ID<mailman.586.1331558040.3037.python-list@python.org>
In reply to#21501
On Tue, Mar 13, 2012 at 12:06 AM, James Elford <fil.oracle@gmail.com> wrote:
> I wonder whether you need to use an exception here rather than a yield
> statement?

Or a return statement, if you're not needing multiple responses.

ChrisA

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.python


csiph-web