EAFP - Easier Ask for Forgiveness than Permission - is a coding style where you perform an action and gracefully handle the error cases through exceptions. It contrasts with the LBYL - Look Before You Leap where you make sure all conditions are met before you perform an action.
Although it sounds like a pure coding style issue I ran into a couple of cases where it made a huge difference.
Let's have an example of both. The most usual and natural style is the LBYL:
It looks pretty sane. We ensure the file exists before removing it.
The other way - EAFP - is to remove the file and deal with the exception that are risen if the file wasn't there:
pass # Handle the exception
This code is a less intuitive but it is a lot more robust.
Why is the EAFP more robust ?
At the beginning the above code was written using the LBYL style.
This part of the code was called in an asynchronous task. What happened is that on a regular basis the os.remove raised exception complaining that filename didn't exist.
At first it was a bit surprising to see the removal line failing when the line right before gave us the ensurance the file was there.
What happened is another thread removed the file in the time between the check and the file removal.
The solution was to switch to the EAFP style for that part of the code. Therefore if the file can be removed it'll be. On the other hand if the file doesn't exist at the exact time we try to remove it an exception is risen and we gracefully handle it.
That's just as easy as it sounds.
About Django ORM
Another area where I've found an interesting use of EAFP is the interaction with Django's ORM.
If you aren't familiar with the ORM, there are chances you have written or you will write something similar to this code:
first_item = None
first_item = queryset.all()
With the Django ORM this will be one or two database requests depending on the number of entries - 0 or at least one respectively. In this snippet permission is asked: it checks if we have at least one object and then get that item.
In our context we usually had several entries so this code snippet costed 2 database queries. Multiply this by the times this function is called and that makes a couple of requests.
An alternative implementation could be:
first_item = None
items = list(queryset.all())
first_item = items
The issue here is that we get all the queryset items which is way more than what we actually need. Depending on how many items the queryset has and how large the model is it may impact a lot performances.
The best implementation I've found is to use the EAFP style:
first_model = queryset.all()
first_model = None
We are down to a single request and we'll only get the item we need.