[Python-ideas] exception based conditional expression, similar to if-else conditional expression

ilya ilya.nikokoshev at gmail.com
Fri Aug 21 22:34:06 CEST 2009


First, I'd like to thank you for taking time to write this as a very
concise post. Your idea sounds interesting.

But I don't think I saw any reasonable (= readable) syntax.

Anything with bare semicolon is clearly confusing because bare
semicolon has a well-defined meaning as ending a line and starting new
block.

Anything with bare except clause isn't good because catching all
exceptions is a wrong style.

Anything that encourages the use of \ line continuation character
clearly contradicts the well-established guideline that this character
should be used as a last resort.

-1 on either of above suggestions.


But there are deeper problems even in the "simple example".  To see
them, let me show how this example should look in programming style I
strive for:

def safe_float(string: 'any string', invalid = float('nan')) -> 'float':
    '''Wrapper around float() that converts all invalid values to given param.

    Use with care --- this will also catch invalid inputs and other cases
    where notifying the user with exception is a better idea.

    '''
    try:
        return float(string)
    except ValueError:
        return invalid
...
class tests:
    def testSafeFloat(self):
        assert safe_float('5.7') == float('5.7')
        assert safe_float('invalid') == float('nan')
...

# Here string comes from an experiment and I know it's either empty or should
# be valid.
xs1 = (float(string or 'nan') for string in strings)

# Here string comes from a user input. Tell him if he types something wrong.
for string in strings:
    try:
        xs2[i] = float(string)
    catch ValueError:
        string = input('Please retype string #{}'.format(i))
        ...

# Here strings are a result of series of experiments. Unfortunately we don't
# know what format the researchers used for 'no data' or 'invalid data'. So
# for now we'll just change all of these values to 'nan'.
xs3 = (safe_float(string) for string in strings)


Your proposed solution using exception-based conditional expression:

# Here strings are a result of series of experiments. Unfortunately we don't
# know what format the researchers used for 'no data' or 'invalid data'. So
# for now we'll just change all of these values to 'nan'.
# But, unfortunately, if in the future we'll decide to use some different
# function to process data, we'll have to rewrite this whole cycle...
xs = ((float(string) except ValueError: float('nan')) for string in strings)


I think from my examples you get what I'm trying to say -- when you're
thinking about easily catching exceptions, you're either ok with something
simpler or you're actually trying to apply a function. So why not do it
explicitely?

Ilya.

On Thu, Aug 20, 2009 at 3:07 AM, Jeff McAninch<mcaninch at lanl.gov> wrote:
> I would like to propose an expression, similar to the if-else expression,
> that responds to exceptions.
>
> I had originally posted this (probably mistakenly) on py-dev.  This current
> posting is a cleaned up
> version of the idea, based on responses I got on from the earlier posting.
>
> Abstract:
> Proposal for a conditional expression, similar to the if-else expression,
> that responds to exceptions.
>
> Motivation:
> An expression syntax that responds to exceptions, and which reproduces the
> readability and conciseness of the if-else conditional expression, would
> simplify some exception-handling cases, especially within list
> comprehensions.
>
> Very Simple Example - type coercion:
> Current approach:
>     try:
>         x = float(string)
>     except:
>         x = float('nan')
>
> Proposed solution using exception-based conditional expression:
>     x = float(string) except ValueError: float('nan')
>
>
> Simple Example - type coercion in a list comprehension:
> Current approach:
>     def safe_float(string):
>         try:
>             x = float(string)
>         except ValueError:
>             x = float('nan')
>         return x
>     ...
>     xs = (safe(float(string)) for string in strings)
>
> Proposed solution using exception-based conditional expression:
>     xs = ((float(string) except ValueError: float('nan')) for string in
> strings)
>
> Discussion:
> In my own python coding, I find I make common use of the if-else conditional
> expression, especially within list comprehensions.  (In one of my packages,
> which has ~5800 lines of code, I found if-else expressions in ~1% of the
> lines.)
>
> Here is a slightly more involved example than the examples presented above.
> In data processing, I often string together a sequence of iterable list
> comprehensions, corresponding to a sequence of operations on a given dataset
> "ys" to produce a processed dataset "x":
>     xs = (operation_A(x) for x in ys)
>     xs = (operation_B(x) for x in xs if filter_B(x))
>     xs = (operation_C(x) if (some_condition(x)) else operation_D(x) for x in
> xs if filter_C(x))
>     # final, explicit list of values
>     xs = [ x for x in xs ]
> This is often a flexible way for me to define processing and filtering
> sequences which also seems
> to have good performance on very large datasets.  One advantage is that I
> can quickly mix-and-match from existing processes like this to make a new
> process.  An exception-based conditional would go nicely
> into many of these process sequences, keeping them both robust and flexible.
>     xs = (operation_N(x) except exceptionN: operation_Nprime(x) for x in xs)
>
> I also often have object classes which have some common method or
> attribute.  For instance, some of my objects have scope-dependent values:
>     x = y.evaluate(scope))
> where scope is typically locals(), globals(), or some other dictionary-like
> container.  But, to keep my code modular, I want to handle, in the same
> lines of code, objects which do not have some particular method, which leads
> me to lines of code like:
>     x = y.evaluate(locals()) if ('evaluate' in y.__dict__) else y
> This seems not very "Pythonic", similar to using type-testing instead of
> try-except.  (My impression was that there was a long-standing trend in the
> evolution of Python to remove tests like this, and I thought that was the
> original motivation for the try-except syntax.)
>
> I would much rather write:
>     x = y.evaluate(locals()) except AttributeError: y
> or, in the list comprehension example:
>     xs = (y.evaluate(locals()) except AttributeError: y for y in ys)
>
> Clearly this can be handled in several ways with the language as it is.  One
> way is to define a new function, as in the second simple example above:
>     def safe_evaluate(y,scope):
>        try:
>           x = y.evaluate(scope)
>        except AttributeError:
>           x = y
>        return x
>     ...
>     xs = (safe_evaluate(y,locals()) for y in ys)
> but this quickly (in my packages at least) leads to an annoying
> proliferation of "safe_" functions.
> Again, this seems not to be in the "Pythonic" spirit, and is also less
> concise, less readable.  (I also suspect, but have not verified, that this
> is in general less efficient than in-line expressions -- wasn't that part of
> the original motivation for list comprehensions?).
>
> In the thread of my previous post to py-dev, there were comments, questions,
> and suggestions concerning the details of the syntax.  Having reflected on
> this for a couple weeks, I am now most strongly supportive of what is
> essentially just an inline compression of the current try-except syntax.  So
> the following examples would be allowed:
>     x = expression0 except: default_expression
>     x = expression0 except exception1: expression1 except exception2:
> expression2 except: default_expression
>
> Or, more generally:
>     x = expression0\
>             except exception1: expression1\
>             except exception2: expression2\
>             ...
>             except exceptionI: expressionI\
>             ...
>             except: default_expression
> In this last example, the behaviour would be as follows:
>     - evaluate expression0.
>             If no exception is encountered, return the result.
>     - if an exception is encountered,
>             search for the matching exception in the except clauses.
>     - if a matching exception ("exceptionI") is found,
>             evaluate the corresponding expression ("expressionI"), and
> return the result.
>     - if no matching exception is found, and a default except: clause (i.e.,
> one without and exception)
>           is given, evaluate default_expression, and return the result.
>     - if no matching exception is found, and no default except clause if
> given,
>           pass the exception on to the caller.
>     - if a new exception is encountered while evaluating an an except
> expression ("expressionI"),
>           pass the exception on to the caller.
>
> I hope I have made a convincing case here.  This seems to me to be a natural
> ("Pythonic") addition to the language.
>
> Jeff McAninch
>
> --
> ==========================
> Jeffrey E. McAninch, PhD
> Physicist, X-2-IFD
> Los Alamos National Laboratory
> Phone: 505-667-0374
> Email: mcaninch at lanl.gov
> ==========================
>
> _______________________________________________
> Python-ideas mailing list
> Python-ideas at python.org
> http://mail.python.org/mailman/listinfo/python-ideas
>
>



More information about the Python-ideas mailing list