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?
“Ask forgiveness, not permission” means doing the appropriate thing even if you don't have any backing. It's not about being reckless but about knowing when to push the envelope. It's about not waiting for anyone's approval to do the things you need to do in order to succeed.
It's Easier to Ask Forgiveness Than It Is To Get Permission But Not In An HOA. It's Easier to Ask Forgiveness Than It Is To Get Permission is a quote from the late Rear Admiral Grace Hopper, who was a U.S. Naval officer.
But what should we do after we fail and let people down? In the Bible there is a theme of forgiveness—both asking for it and giving it. Asking for forgiveness from God and others if we have wronged them is important. Not only does it show repentance and obedience, but it also shows a witness for Christ.
What Admiral Grace Hopper really meant when she said, "It's easier to ask forgiveness than it is to get permission"
“Ask forgiveness, not permission” opposes two programming styles. “Ask for permission” goes like this:
if can_do_operation(): perform_operation() else: handle_error_case()
“Ask forgiveness” goes like this:
try: perform_operation() except Unable_to_perform: handle_error_case()
This is a situation where it is expected that attempting to perform the operation might fail, and you have to handle the situation where the operation is impossible, one way or another. For example, if the operation is accessing a file, the file might not exist.
There are two main reasons why it's better to ask for forgiveness:
can_do_operation()
and the time when you run perform_operation()
. So you'd have to handle the error anyway.What ask-forgiveness situations have in common is that you're attempting to perform an operation, and you know that the operation may fail.
When you write foo.bar
, the non-existence of bar
is not normally considered a failure of the object foo
. It's usually a programmer error: attempting to use an object in a way that it wasn't designed for. The consequence of a programmer error in Python is an unhandled exception (if you're lucky: of course, some programmer errors can't be detected automatically). So if bar
is an optional part of the object, the normal way to deal with this is to have a bar
field that's initialized to None
, and set to some other value if the optional part is present. To test whether bar
is present, write
if foo.bar is not None: handle_optional_part(foo.bar) else: default_handling()
You can abbreviate if foo.bar is not None:
to if foo.bar:
only if bar
will always be true when interpreted as a boolean — if bar
could be 0, []
, {}
or any other object that has a false truth value, you need the is not None
. It's also clearer, if you're testing for an optional part (as opposed to testing between True
and False
).
At this point you may ask: why not omit the initialization of bar
when it's not there, and test its presence with hasattr
or catch it with an AttributeError
handler? Because your code only makes sense in two cases:
bar
field;bar
field that means what you think it means.So when writing or deciding to use the object, you need to make sure that it doesn't have a bar
field with a different meaning. If you need to use some different object that has no bar
field, that's probably not the only thing you'll need to adapt, so you'll probably want to make a derived class or encapsulate the object in another one.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With