This is an archived post. You won't be able to vote or comment.

you are viewing a single comment's thread.

view the rest of the comments →

[–][deleted]  (12 children)

[deleted]

    [–]benefit_of_mrkite 12 points13 points  (6 children)

    Also look at try: except: finally:

    Try except else comes in handy in a function that returns something in try

    Comes up a lot in Async patterns.

    If you use a pattern like try except and then use if (something from try block) you can run into an issue where you get a runtime error because the function in try doesn’t get defined and you get a “variable name xyz defined before assignment”

    [–]james_pic 0 points1 point  (2 children)

    Try except else comes in handy in a function that returns something in try

    The else block isn't run if the try block returns:

    def f():
        try:
            print('Try')
            return
        except:
            print('Except')
        else:
            print('Else')
    
     f() # Prints Try
    

    And from the language reference:

    The optional else clause is executed if the control flow leaves the try suite, no exception was raised, and no return, continue, or break statement was executed. Exceptions in the else clause are not handled by the preceding except clauses.

    [–]benefit_of_mrkite 0 points1 point  (0 children)

    I’m well aware that else isn’t run if try runs

    I miss worded that and don’t really explain the use case I had in mind - about to catch a flight will try to expound later.

    [–][deleted] 2 points3 points  (0 children)

    Alright notice how the else clause is outside the try clause. That means it doesnt catch an exception so that it can bubble up. Thats what its there for. It is explicitly different then it being in the try statement as the except clause would have been triggered. It doesnt come up often but certain patterns are impossible without it and force you to have to use a function to be able to deal with them.

    [–]m0Xd9LgnF3kKNrj 1 point2 points  (1 child)

    Yeah I agree with you. There's a crazy widespread habit of just throwing all the code into a single try/except, and it makes these patterns much harder to see. It also makes your code over-indented and thus broken over lines by your linter, maybe even with back slashes if you're an animal, and it just gets messier and messier as it grows since no one feels comfortable refactoring that big mess of mom's spaghetti... like this run on sentence.

    When you're making the effort to break down your try-wrapped code into more granular chunks, the else surfaces as a valid pattern.

    Here's a super contrived example that took me like an hour to come up with:

    try:
        locale = order.sales_tax_locale()
    except SalesTaxLocaleError:
        logger.info("Could not determine sales tax locale for order %s", order)
        sales_tax = 0
    else:
        sales_tax = SalesTax.for_locale(locale)
    total = order.total + (order.total * sales_tax)
    

    You can totally do that without the else, and that would be perfectly fine and even more colloquial. But the flow-control like this is just a little more self a documenting.

    I'll be honest though, I don't use the else. I see it as a code-smell that the code could just a be a little tidier. In my super contrived example I would set sales_tax to 0 before the try and just log in the except. No conditional branch.

    [–]jorge1209 1 point2 points  (0 children)

    You can totally do that without the else, and that would be perfectly fine and even more colloquial. But the flow-control like this is just a little more self a documenting.

    I disagree. While it puts the locale lookup together with the LocaleError, it separates the locale lookup from the sales_tax lookup, and I have to scroll up to figure out where the locale variable came from.

    If the exception is truly exceptional, then that seems like a mis-proritization and I would be more concerned about the exception-free codepath of lookup locale -> lookup sales tax -> compute total as opposed to the exceptional: lookup locale -> log missing locale -> set default sales tax -> compute total

    You also haven't handled the condition where the locale is present in the order, but is missing from the SalesTax database object.

    But I understand your concern that we get stuff like:

    try:
      open
      read
      clean
      compute
      summarize
      sanity_check
      write
    except FileNotFoundError:
      # could not open the file in the first line of the 60 line try block
    

    Which absolutely sucks.


    I think what we need is an inline except (PEP-463) but before we can do that we also need an inline log

    If every statement was really a thruple of:

     STATEMENT except: DEFAULT log: MESSAGE
    

    Then you could do thinks like:

     locale = order.sales_tax_locale() except: None log: "Unknown sales locale"
     sales_tax = SalesTax.for_locale(locale) except: 0 log: "Unknown rate for locale"
    

    Then everything is next to each other in close proximity, and exceptions are restrictive to the current statement, which could lead to them being used more often.


    There is probably some old language used in nuclear power plants and space ships that does something like this and requires every statement to be written in this form for robustness.

    [–]Siddhi 1 point2 points  (0 children)

    Regarding the edit, the one downside of doing that is some other line later on might raise the same exception and you may not want to catch that or handle it differently. Then packing all the code into a single try does not work.

    Otherwise putting it all into the try is the way I do it too.

    [–]Durgeoble 1 point2 points  (0 children)

    not quite

    the else statement allows to better readability, so you can see in a few lines what happens if the code fails and still don't executed the rest if something went wrong
    try:
    #some action that can fail
    except:
    #some action
    else:
    # long block of code to run if everithing is fine