'Multiple try blocks with similar code and a single exception
I have a set of slightly different operations I want to perform on a bunch of different files, some of which may or may not exist at any given time. In all cases, if a file doesn't exist (returning FileNotFoundError), I want it to just log that that particular file doesn't exist, then move on, i.e. identical exception handling for all FileNotFoundError cases.
The tricky part is that what I want to do with each set of file is slightly different. So I can't simply do:
for f in [f1, f2, f3, f4]:
try:
do_the_thing(f)
except FileNotFoundError:
print(f'{f} not found, moving on!')
Instead I want something like:
try:
for f in [f_list_1]:
do_thing_A(f)
for f in [f_list_2]:
do_thing_B(f)
for f in [f_list_3]:
do_thing_C(f)
except FileNotFoundError:
print(f'{f} not found, moving on!')
But where each for block is tried regardless of success / failure of previous block.
Obviously I could use a whole lot of separate try-except sets:
for f in [f_list_1]:
try:
do_thing_A(f)
except FileNotFoundError:
print(f'{f} not found, moving on!')
for f in [f_list_2]:
try:
do_thing_B(f)
except FileNotFoundError:
print(f'{f} not found, moving on!')
for f in [f_list_3]:
try:
do_thing_C(f)
except FileNotFoundError:
print(f'{f} not found, moving on!')
But is there a more elegant or Pythonic way to do this, given that it's the same exception handling in each case?
Solution 1:[1]
I think I would start with something like this that wraps your functions.
def do_thing_A(f):
print(f"do_thing_A({f})")
def do_thing_B(f):
print(f"do_thing_B({f})")
def do_by_thing(fn, file_path):
try:
fn(file_path)
except FileNotFoundError:
print(f'{file_path} not found, moving on!')
tasks = [
(do_thing_A, ["hello", "word"]), #(function, list_of_paths)
(do_thing_B, ["foo", "bar"]),
]
for fn, file_list in tasks:
for file_path in file_list:
do_by_thing(fn, file_path)
Having done so though I think this begs the question of decorators. Maybe something like:
import os
def if_file_exists(fn):
def _inner(f):
if not os.path.isfile(f):
print(f'{f} not found, moving on!')
return
fn(f)
return _inner
@if_file_exists
def do_thing_A(f):
print(f"do_thing_A({f})")
@if_file_exists
def do_thing_B(f):
print(f"do_thing_B({f})")
tasks = [
(do_thing_A, ["./results.csv", "word"]), #(function, list_of_paths)
(do_thing_B, ["./sample.txt", "bar"]),
]
for fn, file_list in tasks:
for file_path in file_list:
fn(file_path)
Of course, either option could be altered to use exceptions or os.path.isfile(). I just did one each way.
If you decided that wrappers and decorators was overkill, then you could also just simply do:
def do_thing_A(f):
print(f"do_thing_A({f})")
def do_thing_B(f):
print(f"do_thing_B({f})")
tasks = [
(do_thing_A, ["hello", "word"]), #(function, list_of_paths)
(do_thing_B, ["foo", "bar"]),
]
for fn, file_list in tasks:
for file_path in file_list:
try:
fn(file_path)
except FileNotFoundError:
print(f'{file_path} not found, moving on!')
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |
