Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use an expression twice in one line - as a condition AND for string formatting?

I find that in lots of different projects I'm writing a lot of code where I need to evaluate a (moderately complex, possibly costly-to-evaluate) expression and then do something with it (e.g. use it for string formatting), but only if the expression is True/non-None.

For example in lots of places I end up doing something like the following:

result += '%s '%( <complexExpressionForGettingX> ) if <complexExpressionForGettingX> else ''

... which I guess is basically a special-case of the more general problem of wanting to return some function of an expression, but only if that expression is True, i.e.:

f( e() ) if e() else somedefault

but without re-typing the expression (or re-evaluating it, in case it's a costly function call).

Obviously the required logic can be achieved easily enough in various long-winded ways (e.g. by splitting the expression into multiple statements and assigning the expression to a temporary variable), but that's a bit grungy and since this seems like quite a generic problem, and since python is pretty cool (especially for functional stuff) I wondered if there's a nice, elegant, concise way to do it?

My current best options are either defining a short-lived lambda to take care of it (better than multiple statements, but a bit hard to read):

(lambda e: '%s ' % e if e else '')( <complexExpressionForGettingX> )

or writing my own utility function like:

def conditional(expr, formatStringIfTrue, default='')

... but since I'm doing this in lots of different code-bases I'd much rather use a built-in library function or some clever python syntax if such a thing exists

like image 801
Ben Spiller Avatar asked Jan 15 '13 11:01

Ben Spiller


People also ask

What does __ format __ do in Python?

The __format__ method is responsible for interpreting the format specifier, formatting the value, and returning the resulting string. It is safe to call this function with a value of “None” (because the “None” value in Python is an object and can have methods.)

What is str format () in Python?

Python's str. format() method of the string class allows you to do variable substitutions and value formatting. This lets you concatenate elements together within a string through positional formatting.

Can you use or twice in Python?

Can you use or twice in Python? Using multiple OR operator example The OR operator is used twice in the if statement to evaluate three expressions. If either of the three expressions is True, the print function inside the if statement should display the message.


4 Answers

I like one-liners, definitely. But sometimes they are the wrong solution.

In professional software development, if the team size is > 2, you spent more time on understanding code someone else wrote than on writing new code. The one-liners presented here are definitely confusing, so just do two lines (even though you mentioned multiple statements in your post):

X = <complexExpressionForGettingX>
result += '%s '% X  if X else ''

This is clear, concise, and everybody immediately understands what's going on here.

like image 99
Thorsten Kranz Avatar answered Oct 20 '22 22:10

Thorsten Kranz


Python doesn't have expression scope (Is there a Python equivalent of the Haskell 'let'), presumably because the abuses and confusion of the syntax outweigh the advantages.

If you absolutely have to use an expression scope, the least worst option is to abuse a generator comprehension:

result += next('%s '%(e) if e else '' for e in (<complexExpressionForGettingX>,))
like image 31
ecatmur Avatar answered Oct 20 '22 23:10

ecatmur


You could define a conditional formatting function once, and use it repeatedly:

def cond_format(expr, form, alt):
    if expr:
        return form % expr
    else:
        return alt

Usage:

result += cond_format(<costly_expression>, '%s ', '')
like image 37
Ber Avatar answered Oct 20 '22 22:10

Ber


After hearing the responses (thanks guys!) I'm now convinced there's no way to achieve what I want in Python without defining a new function (or lambda function) since that's the only way to introduce a new scope.

For best clarity I decided this needed to be implemented as a reusable function (not lambda) so for the benefit of others, I thought I'd share the function I finally came up with - which is flexible enough to cope with multiple additional format string arguments (in addition to the main argument used to decide whether it's to do the formatting at all); it also comes with pythondoc to show correctness and illustrate usage (if you're not sure how the **kwargs thing works just ignore it, it's just an implementation detail and was the only way I could see to implement an optional defaultValue= kwarg following the variable list of format string arguments).

def condFormat(formatIfTrue, expr, *otherFormatArgs, **kwargs):
""" Helper for creating returning the result of string.format() on a 
specified expression if the expressions's bool(expr) is True 
(i.e. it's not None, an empty list  or an empty string or the number zero), 
or return a default string (typically '') if not. 

For more complicated cases where the operation on expr is more complicated 
than a format string, or where a different condition is required, use:
(lambda e=myexpr: '' if not e else '%s ' % e)

formatIfTrue -- a format string suitable for use with string.format(), e.g. 
    "{}, {}" or "{1}, {0:d}". 
expr -- the expression to evaluate. May be of any type. 
defaultValue -- set this keyword arg to override

>>> 'x' + condFormat(', {}.', 'foobar')
'x, foobar.'

>>> 'x' + condFormat(', {}.', [])
'x'

>>> condFormat('{}; {}', 123, 456, defaultValue=None)
'123; 456'

>>> condFormat('{0:,d}; {2:d}; {1:d}', 12345, 678, 9, defaultValue=None)
'12,345; 9; 678'

>>> condFormat('{}; {}; {}', 0, 678, 9, defaultValue=None) == None
True

"""
defaultValue = kwargs.pop('defaultValue','')
assert not kwargs, 'unexpected kwargs: %s'%kwargs
if not bool(expr): return defaultValue

if otherFormatArgs:
    return formatIfTrue.format( *((expr,)+otherFormatArgs) )
else:
    return formatIfTrue.format(expr)
like image 1
Ben Spiller Avatar answered Oct 21 '22 00:10

Ben Spiller