在一行中使用一个表达式两次 - 作为字符串格式的条件和?
Posted
技术标签:
【中文标题】在一行中使用一个表达式两次 - 作为字符串格式的条件和?【英文标题】:Use an expression twice in one line - as a condition AND for string formatting? 【发布时间】:2012-12-29 12:31:51 【问题描述】:我发现在许多不同的项目中,我编写了很多代码,我需要评估一个(中等复杂,可能评估成本高)表达式,然后用它做一些事情(例如,将它用于字符串格式化),但前提是表达式为 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 语法(如果存在这样的东西)
【问题讨论】:
result += '%s '%( <complexExpressionForGettingX> or '')
怎么样?
@wim - 返回' '
表示否定表达式。 OP想要''
啊,你是对的,哎呀。
【参考方案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)
【讨论】:
【参考方案2】:Python 没有表达式范围 (Is there a Python equivalent of the Haskell 'let'),大概是因为语法的滥用和混淆超过了优点。
如果您绝对必须使用表达式范围,最糟糕的选择是滥用生成器理解:
result += next('%s '%(e) if e else '' for e in (<complexExpressionForGettingX>,))
【讨论】:
+1。虽然我认为它比 OP 提供的 lambda 东西更复杂,但它是一个有效且不错的替代方案,值得在更复杂的情况下考虑。 -1:这比为中间结果想一个名字好?这是令人困惑和曲折的,无论如何都需要你想出一个名字。只需将其拆分为两个语句。 @NedBatchelder 添加语句并不总是有意义的,例如如果作用域表达式本身嵌入在条件中。当然,在这种情况下,无论如何重构都可能是一个好主意。 虽然我不相信这种推导式的使用和我原来的 lambda 表达式一样清晰,+1 表示 python 没有表达式级范围(只有类和函数)的重要和有用的观察定义范围,加上诸如 lambdas/comprehensions 之类的东西,这是一种幕后的功能)。这解释了为什么在 Python 中,除了定义一个函数(或 lambda 函数)之外,没有其他方法可以做我想做的事情。【参考方案3】:大概,你想这样做重复来建立一个字符串。从更全局的角度来看,您可能会发现 filter
(或 itertools.ifilter
)对值的集合做了您想做的事情。
你会得到这样的结果:
' '.join(map(str, filter(None, <iterable of <complexExpressionForGettingX>>)))
使用None
作为filter
的第一个参数表示接受任何真值。作为一个简单表达式的具体示例:
>>> ' '.join(map(str, filter(None, range(-3, 3))))
'-3 -2 -1 1 2'
根据您计算值的方式,等效列表或生成器理解可能更具可读性。
【讨论】:
【参考方案4】:你可以定义一次条件格式化函数,然后重复使用它:
def cond_format(expr, form, alt):
if expr:
return form % expr
else:
return alt
用法:
result += cond_format(<costly_expression>, '%s ', '')
【讨论】:
是的,我会给你 +1 写出来,尽管我确实在我的问题中提出了这种可能的方法。我还添加了一个更完整和功能齐全的 condFormat 方法作为我对问题的回答【参考方案5】:我绝对喜欢单线。但有时它们是错误的解决方案。
在专业软件开发中,如果团队规模 > 2,那么您花在理解别人编写的代码上的时间比编写新代码的时间要多。此处介绍的单行语句肯定令人困惑,因此只需执行两行(即使您在帖子中提到了多个语句):
X = <complexExpressionForGettingX>
result += '%s '% X if X else ''
这很清楚,简洁,每个人都会立即明白这里发生了什么。
【讨论】:
-1: 嗯,是的,有时它们是错误的,但其他时候(特别是当您需要在函数中多次执行时)在单个表达式中执行操作比在命名空间中引入一个虚假的短期变量(例如 X)并占用源文件的两行来执行单个操作(即使是“专业软件开发”的恕我直言!) - 我认为在我的情况下,这种方法会阻碍可读性,尤其是与定义帮助函数来解决这种常见情况相比。我也在我的问题中明确表示我考虑过这种方法。 也许一个有意义的变量名会改进这个策略。在这种情况下,多余的行几乎是注释。 是的,你绝对是对的!我选择 X 只是因为选择了 OP,并且由于缺乏上下文,X 可能是什么。有时我什至为1
之类的数字定义变量,只是出于文档的原因,例如correction_for_offset = 1
。这使得代码更容易理解和维护。以上是关于在一行中使用一个表达式两次 - 作为字符串格式的条件和?的主要内容,如果未能解决你的问题,请参考以下文章