"Ask Forgiveness Not Permission" - Explain

Ask forgiveness not permission - explain

The classical "ask forgiveness not permission" example is accessing values from a dict that may not exist. E.g.:

names = { 'joe': 'Joe Nathan', 'jo': 'Jo Mama', 'joy': 'Joy Full' }
name = 'hikaru'

try:
print names[name]
except KeyError:
print "Sorry, don't know this '{}' person".format(name)

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:

if name in names:
print names[name]
else:
print "Sorry, don't know this '{}' person".format(name)

or

real_name = names.get(name, None)
if real_name:
print real_name
else:
print "Sorry, don't know this '{}' person".format(name)

Such examples of "ask forgiveness" are often too simple. IMO it's not crystal clear that try/except blocks are inherently better than if/else. The real value is much clearer when performing operations that might fail in a variety of ways--such as parsing; using eval(); 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/except blocks around every variable usage. That would be horrible. And you don't need to set self.bar in your __init__() since it's set in your class definition 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 None is 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.

What is the EAFP principle in Python?

From the glossary:

Easier to ask for forgiveness than permission. This common Python coding style assumes the existence of valid keys or attributes and catches exceptions if the assumption proves false. This clean and fast style is characterized by the presence of many try and except statements. The technique contrasts with the LBYL style common to many other languages such as C.

An example would be an attempt to access a dictionary key.

EAFP:

try:
x = my_dict["key"]
except KeyError:
# handle missing key

LBYL:

if "key" in my_dict:
x = my_dict["key"]
else:
# handle missing key

The LBYL version has to search the key inside the dictionary twice, and might also be considered slightly less readable.

To ask permission or apologize?

Probably not. .NET exceptions are relatively expensive.

Several .NET functions offer both variants for this reason. (int.TryParse, which returns a success code is often recommended because it is faster than int.Parse which throws an exception on failure)

But the only answer that matters is what your own profiling data tells you.
If you need performance, then you need to measure, measure, measure.

Because what was fastest on my computer, with my code, with my version of the .NET framework, at this time may not be the fastest on your computer, with your code, with your version of the .NET framework at the time when you read it.

Most simple way to check object exist and give responce

I think this is the pythonic way ("Ask forgiveness, not permission") to check the object existence bu using try..except clause

def accept(self, request, pk):
try:
Company.objects.get(pk=pk)
return Response({"message": "Success"})
except Company.DoesNotExist:
return Response({"message": "Error"}, status=status.HTTP_405_METHOD_NOT_ALLOWED)




You can find a nice SO post here which ask samething in Django perspective, which says exists() is faster than try..except



So, you could rewrite your code something like,

def accept(self, request, pk):
if Company.objects.filter(pk=pk).exists():
return Response({"message": "Success"})
return Response({"message": "Error"}, status=status.HTTP_405_METHOD_NOT_ALLOWED)

Catch AttributeError only on a specific attribute

First of all, like all the coding zen statements, the forgiveness vs permission principle is not an absolute one. In this case in particular you know for sure that the first that will happen inside the try/except block is the raise of the AttributeError, but you might find yourself in situations where the AttributeError is raised after additional operations that are costly or have side effects.

def some_function(foo):
costly_operation()
insert_something_to_a_database(foo)
return foo.bar + foo.baz

try:
some_function(foo)
except AttributeError:
# All the work done was not needed and it may even have
# left traces (like the db insert) that now need to be undone.

In these cases, it is simpler, safer more readable and more efficient to check for permission before executing anything rather than performing unneeded operations or even trying to fix the mess afterwards.

If, anyways, you really need to ask for forgiveness rather than permission you can simply check for permission afterwards: You go ahead assuming that everything is right and, if there is an error, you figure out if it was the one that you want to raise or not.

try:
some_function(foo)
except AttributeError:
if not hasattr(foo, 'bar'):
raise


Related Topics



Leave a reply



Submit