Like the articles? Buy the book! Dead Simple Python by Jason C. McDonald is available from No Starch Press.
Exceptions.
One of the arch-nemese...
For further actions, you may consider blocking this person and/or reporting abuse
Very informative, well done!
raise from
is one of my favorite features, it makes tracebacks so much better.I don't think I've recently used the
else
clause in exception handling but thanks for reminding me it's there :DThis is great! Thank you so much for the detailed walkthrough!
Question: I had never seen the
super
syntax used when defining custom exceptions. I've always just defined them as empty classes inheriting from a parent class (either Exception or one of my other custom exceptions.When I do that, I get to see the message, but when I pass-through the
message
using the__init__
method like you've got shown, it looks different.It seems almost better to just declare an empty class with
pass
to me. Is that wrong? Are there downsides? The official docs aren't super clear as to what's more common/expected.I'd be curious how this behaves in terms of actually catching the exception with
try...except
, and what information would be available toe
inexcept GloopException as e:
? To be honest, I really don't know; I only understand that the standard is to usesuper()
.I'll ask the Python friends who have been editing this series if they have any insight. ;)
Okay, so after a bit of discussion in
#python
on Freenode IRC, we've come to this verdict:Any time you have an
__init__()
and you've inherited, callsuper().__init__()
. Period. However...There are some camps that argue that if you don't otherwise need an
__init__()
(other than to call super), don't bother with it at all. Others, including myself, believe in always explicitly declaring an initializer withsuper()
. I don't know that there's a clear right or wrong here. They both work out the same.The weird behavior in your code is because there's a typo...which honestly came from me. You should NOT pass
self
tosuper().__init__(self, message)
! That should instead readsuper().__init__(message)
. (I've fixed that in my article.)Thanks to
altendky
,dude-x
,_habnabit
,TML
, andYhg1s
of Freenode IRC's#python
for the insight.Ooooohkay, gotcha. That makes total sense. Yeah, I think I fall in the camp of not showing the
__init__
unless I'm extending it (because I think it looks prettier), but I could see the "explicit is better" argument for the other way.The most important thing is that you showed me the right way to extend if I need to, which I really appreciate!
Yeah, definitely. Good to know what is standard to use. Thanks so much! :)
Incidentally, I'm trying to find out more, because there seems to be some debate! (It's Python, so of course there is.)
Great article. Would not have guessed that try...except would be more efficient than if/else in cases like you mentioned, although that makes sense.
Any idea what the logic is for having finally: run regardless of returns?
Great info about "except WhateverError as e:" and "raise from".
Edit: Also always great reading the informative comments, both interesting questions and answers!
finally
always runs to ensure cleanup/teardown behavior runs. It comes in handy surprisingly often.Also, whether
try
orif
is more efficient is really really subjective. If it matters, always measure.Good point. But good to even be aware of the idea!
I can see how that would be confusing, but in general, it doesn't matter either way. Switching it like you suggested would change the
Calculation Result:
line, but it wouldn't change the actual function return which is printed out separately.If you swapped those, you'd see:
The important part is that second line; that's printed from the function's return.
Really informative guide, I don't use try//except as often as I should but definitely will "try" to be more efficient and safe.
Also, English is not my mother tongue, but I was wondering if you might have a typo in "finally is running, even after our return statement. The function doesn't exist like it normally would." as in "exits" instead of "exist" ? It makes more sense to me.
Cheers, mate ♥
You are absolutely right! I'm going to fix that now. Great catch.
Thanks for the article. As I'm coming from the C++ world, I'm suspicious when people say that try-except blocks are not so expensive.
And that's true, relatively they are not as expensive as in C++.
But have you actually measured, for example, the cost of an extra key check in a dictionary lookup compared to raising an exception?
This would vary from machine to machine, I guess, but on mine, the failure case is 30-40x slower with the try-raise block compared to the if-else. That is quite significant.
On the other hand, the success case is about 30-50% faster with the try-case block.
30-50% compared to 3000-4000%.
To me, the bottom line is, if performance matters, measure and know your data. Otherwise, you might just mess things up.
It's a good point - you should always measure for your specific scenario.
The
except
is supposed to be the "exceptional" scenario, rather than the rule. In general, thetry
should succeed several times more often than it fails. Contrast that with theif...else
(ask permission) scenario, where we perform the lookup every time, thetry...except
scenario actually winds up being the more efficient approach in most cases. (See this StackOverflow answer.)To put that another way, if you imagine that just the
if...else
andtry...except
structures by themselves are roughly identical in performance, at least typical scenarios, it is the conditional statement inside of theif()
that is the point of inefficiency.I'm oversimplifying, of course, but this can at least get you in the ballpark.
if(i % 2 == 0)
is going to be pretty inexpensive, and would succeed only 50% of the time in any given sequential, so that would be a case where we'd almost certainly use anif...else
for our flow control;try...except
would be too expensive anyway! By contrast, the dictionary lookup is quite a bit more expensive, especially if it succeeds eight times more than it fails. If we know from our data that it will fail more than succeed, however, an "ask permission" approach may indeed be superior.At any rate, yes, measure, and consider the costs of your particular scenario.
P.S. As I mentioned in the article, of course, the
collections.defaultdict
would be considered superior to bothtry...except
andif...else
in the example scenario.