Pythonic style – EAFP versus LBYL

Now let's look at another tenet of Python philosophy and culture, the idea that "It's Easier to Ask for Forgiveness than for Permission".

There are only two approaches to dealing with a program operation that might fail. The first approach is to check that all the preconditions for a failure prone operation are met in advance of attempting the operation. The second approach is to blindly hope for the best, but be prepared to deal with the consequences if it doesn't work out.

In Python culture these two philosophies are known as Look Before you Leap (LBYL) and its Easier to Ask for Forgiveness than for Permission (EAFP) – which, incidentally, was coined by Rear Admiral Grace Hopper, inventor of the compiler.

Python is strongly in favor of EAFP because it puts primary logic for the "happy path"  in its most readable form, with deviations from the normal flow handled separately, rather than interspersed with the main flow.

Let's consider an example – processing a file. The details of the processing aren't relevant. All we need to know is that the process_file() function will open a file and read some data from it.

First, the LBYL version:

import os

p = '/path/to/datafile.dat'

if os.path.exists(p):
process_file(p)
else:
print('No such file as {}'.format(p))

Before attempting to call process_file() we check that the file exists, and if it doesn't we avoid making the call and print a helpful message instead. There are several problems with this approach, some obvious and some insidious. One obvious problem is that we only perform an existence check. What if the file exists but contains garbage? What if the path refers to a directory instead of a file? According to LBYL we should add preemptive tests for those too.

A more subtle problem is that there is a race condition here. It's possible for the file to be deleted, for example by another process, between the existence check and the process_file() call … a classic race condition. There's really no good way to deal with this – handling of errors from process_file() will be needed in any case!

Now consider the alternative, using the more Pythonic EAFP approach:

p = '/path/to/datafile.dat'

try:
process_file(f)
except OSError as e:
print('Could not process file because {}'.format(str(e)))

In this version we attempt the operation without checks in advance, but we have an exception handler in place to deal with any problems. We don't even need to know in a lot of detail exactly what might go wrong. Here we catch OSError which covers all manner of conditions such as file-not-found and using directories where files are expected.

EAFP is standard in Python, and following that philosophy is primarily facilitated by exceptions. Without exceptions, and being forced to use error codes instead, you are required to include error handling directly in the main flow of the logic. Since exceptions interrupt the main flow, they allow you to handle exceptional cases non-locally.

Exceptions coupled with EAFP are also superior because, unlike error codes, exceptions cannot be easily ignored. By default exceptions have a big effect, whereas error codes are silent by default. So the exception-/EAFP-based style makes it very difficult for problems to be silently ignored.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset