我有一个方法,按顺序调用其他4个方法来检查特定条件,并在每次返回Truthy时立即返回(不检查以下方法) .
def check_all_conditions():
x = check_size()
if x:
return x
x = check_color()
if x:
return x
x = check_tone()
if x:
return x
x = check_flavor()
if x:
return x
return None
这似乎是很多 Baggage 代码 . 而不是每个2行if语句,我宁愿做类似的事情:
x and return x
但那是无效的Python . 我在这里错过了一个简单优雅的解决方案吗?顺便说一句,在这种情况下,这四种检查方法可能很昂贵,所以我不想多次调用它们 .
17 回答
我要跳到这里并且从未写过一行Python,但我认为
if x = check_something(): return x
有效吗?如果是这样:
你有没有考虑过只在一行写
if x: return x
?这并不比你的重复少,但IMNSHO它看起来相当平滑 .
你可以使用一个循环:
这具有额外的优势,您现在可以使条件数变量 .
你可以使用map() filter()(Python 3版本,在Python 2中使用future_builtins versions)来获得第一个这样的匹配值:
但如果这更具可读性是值得商榷的 .
另一种选择是使用生成器表达式:
Don't change it
正如各种其他答案所示,还有其他方法可以做到这一点 . 没有一个像你的原始代码一样清晰 .
理想情况下,我会重写
check_
函数以返回True
或False
而不是值 . 然后你的支票变成了假设你的
x
不是不可变的,你的函数仍然可以修改它(虽然它们不能重新分配它) - 但是一个名为check
的函数无论如何都不应该真正修改它 .我已经看到过去使用dicts的一些有趣的switch / case语句实现让我得到了这个答案 . 使用这个例子你've provided you would get the following. (It' s madness
using_complete_sentences_for_function_names
,所以check_all_conditions
被重命名为status
. 见(1))select函数无需调用每个
check_FUNCTION
两次,即通过添加另一个函数层来避免check_FUNCTION() if check_FUNCTION() else next
. 这对于长时间运行的功能很有用 . dict中的lambdas延迟执行它的值直到while循环 .作为奖励,您可以修改执行顺序,甚至可以通过更改
k
和s
来跳过某些测试 .k='c',s={'c':'b','b':None}
减少了测试次数并反转了原始处理顺序 .timeit
研究员可能讨厌在堆栈中添加额外的一层或两层的成本,并且dict的成本会有所提高,但你似乎更关心代码的漂亮 .或者,更简单的实现可能如下:
根据Curly's law,您可以通过分割两个问题来使此代码更具可读性:
我要检查什么?
有一件事是真的吗?
分为两个功能:
这避免了:
复杂的逻辑结构
真的很长
重复
...同时保留线性,易读的流程 .
根据您的特定情况,您可能还可以提供更好的功能名称,这使其更具可读性 .
这是Martijns第一个例子的变种 . 它还使用“callables”样式,以便允许短路 .
您可以使用内置
any
而不是循环 .请注意
any
返回一个布尔值,因此如果您需要检查的确切返回值,此解决方案将不起作用 .any
将不区分14
,'red'
,'sharp'
,'spicy'
作为返回值,它们将全部作为True
返回 .我很惊讶没有人提到为此目的而制作的内置any:
请注意,尽管此实现可能是最清楚的,但它会评估所有检查,即使第一个检查是
True
.如果您确实需要在第一次失败检查时停止,请考虑使用reduce来将列表转换为简单值:
在你的情况下:
lambda a, f: a or f()
是检查累加器a
或当前检查f()
是True
的函数 . 请注意,如果a
是True
,则不会评估f()
.checks
包含检查功能(f
来自lambda的项目)False
是初始值,否则不会进行检查,结果将始终为True
any
和reduce
是函数式编程的基本工具 . 我强烈建议你训练这些以及map这也很棒!对我来说,最好的答案是来自@ phil-frost,然后是@ wayne-werner .
我觉得有趣的是,没有人说过一个函数将返回许多不同数据类型的事实,这将使得必须对x本身的类型进行检查以进行任何进一步的工作 .
所以我会将@ PhilFrost的回复与保持单一类型的想法混合在一起:
请注意
x
作为参数传递,但all_conditions
用作检查函数的传递生成器,其中所有函数都检查x
,并返回True
或False
. 通过使用func
和all_conditions
作为默认值,您可以使用assessed_x(x)
,或者您可以通过func
传递更多个性化生成器 .这样,一旦检查通过,你就会得到
x
,但它总是相同的类型 .在与timgeb有效的答案中,您可以使用括号进行更好的格式化:
pythonic方式是使用reduce(如已提到的人)或itertools(如下所示),但 it seems to me that simply using short circuiting of the or operator produces clearer code
这种方式有点偏离框,但我认为最终结果是简单,可读,并且看起来不错 .
当其中一个函数评估为真值时,基本思想是
raise
异常,并返回结果 . 以下是它的外观:你需要一个
assertFalsey
函数,当其中一个被调用的函数参数计算为truthy时会引发异常:可以修改上述内容,以便为要评估的函数提供参数 .
当然,你需要
TruthyException
本身 . 此异常提供触发异常的object
:您可以将原始功能转换为更通用的功能,当然:
这可能有点慢,因为您同时使用
if
语句并处理异常 . 但是,异常最多只处理一次,因此对性能的影响应该很小,除非您希望运行检查并获得True
值数千次 .除了Martijn的好答案,你可以链
or
. 这将返回第一个truthy值,如果没有真值,则返回None
:演示:
如果你想要相同的代码结构,你可以使用三元语句!
如果你看一下,我认为这看起来很漂亮 .
演示:
我喜欢@ timgeb's . 在此期间,我想补充说,不需要在
return
语句中表达None
,因为会计算or
分离语句的集合,并返回第一个非零,非空,无 - 无,如果没有,然后None
返回是否有None
!所以我的
check_all_conditions()
函数看起来像这样:使用
timeit
和number=10**7
我查看了一些建议的运行时间 . 为了便于比较,我只使用random.random()
函数返回一个字符串或None
基于随机数 . 这是整个代码:以下是结果:
Martijns上面的第一个例子略有不同,它避免了循环内部的if: