Search code examples
python-3.xexception

Python 3 Try/Except best practice?


This is a question about code structure, rather than syntax. I'd like to know what is best practice and why.

Imagine you've got a python programme. You've got a main class like so:

import my_db_manager as dbm

def main_loop():
   people = dbm.read_db()
   #TODO: Write rest of programme doing something with the data from people...
    

if __name__ == '__main__':
    main_loop()

Then you've got several separate .py files for managing interactions with various tables in a database. One of these .py files, my_db_manager looks like this:

def read_db():
    people = []
    db = connection_manager.get_connection()
    cursor = db.cursor()
    try:
        #Database reading statement 
        sql = 'SELECT DISTINCT(name) FROM people'
        cursor.execute(sql)
        results = cursor.fetchall()
        people = [x[0] for x in results]
    except Exception as e:
        print(f'Error: {e}')
    finally:
        return people

In the example above the function read_db() is called from main_loop() in the main class. read_db() contains try/except clauses to manage errors in interacting with the database. While this works fine, The try/except clauses could instead be placed in main_loop() when calling read_db(). They could equally be located in both places. What is 'best practice' when using try/except? using try/except in the db_manager or using it in the main_loop() where you're managing the programmes logic flow, or using it in both places? Bear in mind I'm giving the above specific example but I'm trying to extrapolate a general rule for applying try/except when writing python.


Solution

  • The best way to write try-except -- in Python or anywhere else -- is as narrow as possible. It's a common problem to catch more exceptions than you meant to handle! (Credits: This Answer)

    In your particular case, of course inside the function. It:

    a) creates abstraction from db errors for the main thread b) eliminates the suspicion of db errors in case some other thing caused exception inside of main thread (tho you can see but still it plucks out every last chance) c) enables you to deal with all database related errors at one place. Efficiently and creatively. How are you gonna make list people outside of function in the main thread. It will double the mess.

    Finally you should stick to this minimalism even inside the function although while catering every place where exception could occur as:

    def read_db():
        #Database reading statement 
        sql = 'SELECT DISTINCT(name) FROM people'
        try:
            db = connection_manager.get_connection()
            cursor = db.cursor()
            cursor.execute(sql)
            results = cursor.fetchall()
        except Exception as e:
            print(f'Error: {e}')
            return []
        else:
            return [x[0] for x in results]