I’m not asking for personal “religious” opinions about this philosophy, rather something a bit more technical.
I understand this phrase is one of several litmus tests to see if your code is “pythonic”. But to me, pythonic means clean, simple and intuitive, not loaded with exception handlers for bad coding.
So, practical example. I define a class:
class foo(object):
bar = None
def __init__(self):
# a million lines of code
self.bar = "Spike is my favorite vampire."
# a million more lines of code
Now, coming from a procedural background, in another function I wanna do this:
if foo.bar:
# do stuff
I’ll get an attribute exception if I was impatient and did not do the initial foo = None. So, “ask forgiveness not permission” suggests I should do this instead?
try:
if foo.bar:
# do stuff
except:
# this runs because my other code was sloppy?
Why would it be better for me to add additional logic in a try block just so I can leave my class definition more ambiguous? Why not define everything initially, therfore explicitly grant permission?
(Don’t beat me up about using try/except blocks… I use them everywhere. I just don’t think it’s right to use them to catch my own errors because I wasn’t a thorough programmer.)
Or… do I completely misunderstand the “Ask Forgivess” mantra?
The classical “ask forgiveness not permission” example is accessing values from a
dictthat may not exist. E.g.:Here the exception that might occur (
KeyError) is stated, so that you’re not asking forgiveness for every error that might occur, but only the one that would naturally occur.For comparison, the “ask permission first” approach might look like:
or
Such examples of “ask forgiveness” are often too simple. IMO it’s not crystal clear that
try/exceptblocks are inherently better thanif/else. The real value is much clearer when performing operations that might fail in a variety of ways–such as parsing; usingeval(); accessing operating system, middleware, database, or network resources; or performing complex mathematics. When there are multiple potential failure modes, being prepared to get forgiveness is hugely valuable.Other notes about your code examples:
You do not need to ladle
try/exceptblocks around every variable usage. That would be horrible. And you don’t need to setself.barin your__init__()since it’s set in yourclassdefinition above. It is usual to define it either in the class (if it’s data likely to be shared among all instances of the class) or in the__init__()(if it’s instance data, specific to each instance).A value of
Noneis not undefined, or an error, by the way. It’s a specific and legitimate value, meaning none, nil, null, or nothing. Many languages have such values so programmers don’t “overload”0,-1,''(empty string) or similar useful values.