1

Below are two versions of the same create_dir function that creates a directory from a file path supplied as file_path argument.

The Version A checks if there is a directory in the supplied as a function argument file_path and only then tries to create a directory. The second Version B skips the extra check and feeds the output of the os.path.dirname(file_path) command straight into the os.makedir. Also, please note, that there are three if statements used in Version A, while version B doesn't have any.

Which version A or B is more preferred or more Pythonic?

Version A:

def create_dir(file_path):
    dirname = None
    os_dirname = None
    if file_path:
        dirname = os.path.dirname(file_path)
    if dirname:
        os_dirname = dirname

    if os_dirname:
        try:
            os.makedirs(os_dirname)
        except OSError as e:
            if e.errno != errno.EEXIST:
                raise

Version B

def create_dir(file_path):
    try:
        os.makedirs(os.path.dirname(file_path))
    except Exception as e:
        if e.errno != errno.EEXIST:
            raise(e)
alphanumeric
  • 17,967
  • 64
  • 244
  • 392
  • What's the point of `if dirname: os_dirname = dirname; if os_dirname ... os_dirname`? Isn't that redundant for `if dirname ... dirname`? – wjandrea Jun 14 '21 at 02:20
  • Sidenote, `raise` is a statement, not a function, so remove the parens from `raise(e)` -> `raise e`. Although actually, it should probably be just `raise`, without the `e`. – wjandrea Jun 14 '21 at 02:22
  • Does this answer your question? [Using try vs if in python](https://stackoverflow.com/questions/1835756/using-try-vs-if-in-python) – Gino Mempin Jun 14 '21 at 03:51

1 Answers1

3

Remember: In Python programming, we generally follow Easier to Ask Forgiveness than Permission a.k.a. EAFP, so I'd recommend the second one.

In both the cases, you're ultimately raising the error, why not to use the second one? That way, you won't even have the overhead of thinking about the scenarios when the program can potentially fail and using a bunch of if/else conditions to handle those events.

On a side note, instead of catching Exception class which is more broad in sense, try catching the specific error/exception. Consider following snippet as an example:

>>> SyntaxError.__base__
<class 'Exception'>
>>> OSError.__base__
<class 'Exception'>

You can see that both the SyntaxError, and OSError inherits from the Exception class, if you try to use some incorrect syntax, you'll end up catching SyntaxError which is obviously not a good practice, instead you could have caught specific errors such as OSErrror, IOError, etc.

EDIT: As suggested in the comment, since SyntaxError happens on runtime on very specific scenarios, you can consider following situation when you pass a list to your function:

create_dir(['src'])

The program will raise following TypeError:

Traceback (most recent call last):
  File "C:\Program Files\JetBrains\PyCharm Community Edition 2020.3.5\plugins\python-ce\helpers\pydev\_pydevd_bundle\pydevd_exec2.py", line 3, in Exec
     exec(exp, global_vars, local_vars)
  File "<input>", line 1, in <module>
  File "<string>", line 5, in create_dir
  File "C:\Users\sbkhy\AppData\Local\Programs\Python\Python37\lib\ntpath.py", line 221, in dirname
     return split(p)[0]
  File "C:\Users\sbkhy\AppData\Local\Programs\Python\Python37\lib\ntpath.py", line 183, in split
     p = os.fspath(p)
TypeError: expected str, bytes or os.PathLike object, not list

But since you're catching Exception class, the TypeError will also be caught and raised, which again is not a good practice.

wjandrea
  • 28,235
  • 9
  • 60
  • 81
ThePyGuy
  • 17,779
  • 5
  • 18
  • 45
  • Beside the point, but getting a [`SyntaxError`](https://docs.python.org/3/library/exceptions.html#SyntaxError) at runtime should be rare. It'll only happen on import or if you compile something, like for example, `eval('a a')`. – wjandrea Jun 14 '21 at 02:33
  • @wjandrea, good point, that will happen in a very specific situation, that was just for illustration. May be I should have given more general example. But I thought `SyntexError` will be easier to grasp. – ThePyGuy Jun 14 '21 at 02:36
  • `TypeError` could be a good example. Imagine if `file_path` is accidentally a list or something. – wjandrea Jun 14 '21 at 02:38
  • Why should we care to narrow the broad `Exception` to `OSError` one? Is there any performance hit if we just use `except Exception as e` ? – alphanumeric Jun 14 '21 at 02:42
  • 2
    @alphanumeric, there won't be a performance hit, but if you catch the specific exception, you may be able to handle it more appropriately rather than directly catching the `Exception` class. – ThePyGuy Jun 14 '21 at 02:44
  • 2
    @alpha For one thing, `Exception` doesn't have an `errno` attribute. – wjandrea Jun 14 '21 at 02:59