8

我发现在许多不同的项目中,我正在编写很多代码,我需要评估一个(中等复杂,可能评估成本高昂)表达式,然后用它做一些事情(例如,将它用于字符串格式化),但是仅当表达式为 True/non-None 时。

例如,在很多地方,我最终会执行以下操作:

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

...我想这基本上是想要返回表达式的某些函数的更普遍问题的特例,但前提是该表达式为真,即:

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

但无需重新键入表达式(或重新评估它,以防它是一个昂贵的函数调用)。

显然,所需的逻辑可以通过各种冗长的方式很容易地实现(例如,通过将表达式拆分为多个语句并将表达式分配给临时变量),但这有点蹩脚,因为这似乎是一个相当普遍的问题,并且因为 python 非常酷(特别是对于功能性的东西),我想知道是否有一种漂亮、优雅、简洁的方式来做到这一点?

我目前最好的选择是定义一个短暂的 lambda 来处理它(比多个语句更好,但有点难以阅读):

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

或编写我自己的实用程序函数,例如:

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

...但是由于我在许多不同的代码库中执行此操作,我宁愿使用内置库函数或一些聪明的 Python 语法(如果存在这样的东西)

4

5 回答 5

8

我绝对喜欢单线。但有时它们是错误的解决方案。

在专业的软件开发中,如果团队规模大于 2,那么您花在理解别人编写的代码上的时间比编写新代码的时间要多。这里介绍的单行语句绝对令人困惑,因此只需执行两行(即使您在帖子中提到了多个语句):

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

这是清晰、简洁的,每个人都会立即明白这里发生了什么。

于 2013-01-15T12:27:26.503 回答
4

Python 没有表达式范围(Haskell 'let' 是否有 Python 等价物),大概是因为语法的滥用和混淆超过了优点。

如果您绝对必须使用表达式范围,那么最糟糕的选择是滥用生成器理解:

result += next('%s '%(e) if e else '' for e in (<complexExpressionForGettingX>,))
于 2013-01-15T12:13:18.787 回答
3

您可以定义一次条件格式化函数,然后重复使用它:

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

用法:

result += cond_format(<costly_expression>, '%s ', '')
于 2013-01-15T13:30:20.280 回答
1

在听到回复后(谢谢大家!)我现在确信如果不定义新函数(或 lambda 函数)就无法在 Python 中实现我想要的,因为这是引入新范围的唯一方法。

为了清楚起见,我决定需要将其实现为可重用函数(而不是 lambda),因此为了他人的利益,我想我会分享我最终想出的函数——它足够灵活,可以处理多个额外的格式字符串参数(除了用于决定是否进行格式化的主要参数);它还附带 pythondoc 以显示正确性并说明用法(如果您不确定 **kwargs 的工作原理,请忽略它,它只是一个实现细节,并且是我可以看到实现可选 defaultValue= kwarg 以下的唯一方法格式字符串参数的变量列表)。

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)
于 2013-01-23T14:17:15.217 回答
0

大概,您想重复执行此操作以构建字符串。从更全局的角度来看,您可能会发现filter(or itertools.ifilter) 对值的集合执行了您想要的操作。

你会得到这样的结果:

' '.join(map(str, filter(None, <iterable of <complexExpressionForGettingX>>)))

用作Nonefor 的第一个参数filter表示接受任何真值。作为一个简单表达式的具体示例:

>>> ' '.join(map(str, filter(None, range(-3, 3))))
'-3 -2 -1 1 2'

根据您计算值的方式,等效列表或生成器理解可能更具可读性。

于 2013-01-15T14:18:02.157 回答