我有多个昂贵的函数可以返回结果。如果所有检查都成功,我想返回所有检查结果的元组。但是,如果一次检查失败,我不想调用后面的检查,例如and
的短路行为。我可以嵌套if
语句,但是如果有很多检查,这将失控。如何在保存结果供以后使用的同时获得and
的短路行为?
def check_a():
# do something and return the result,
# for simplicity, just make it "A"
return "A"
def check_b():
# do something and return the result,
# for simplicity, just make it "B"
return "B"
...
这不会发生短路:
a = check_a()
b = check_b()
c = check_c()
if a and b and c:
return a, b, c
如果检查很多,这很麻烦:
if a:
b = check_b()
if b:
c = check_c()
if c:
return a, b, c
有更短的方法吗?
答案 0 :(得分:27)
只需使用普通的旧循环:
public int example(int[] array) {
int result = 15;
int i = array.length;
while(i > 1)
{
for(int x = 0; x < array.length;x++)
{
result+= 1;
result+=2*array[x];
}
i = i/2;
}
return result;
}
结果将是函数名称与其返回值的映射,您可以在循环中断后使用值执行所需操作。
如果要对所有函数都返回真实结果的情况进行特殊处理,请在for循环中使用results = {}
for function in [check_a, check_b, ...]:
results[function.__name__] = result = function()
if not result:
break
子句。
答案 1 :(得分:8)
编写一个可以运行可迭代函数的函数。调用每个结果并将结果附加到列表中,如果结果为None
,则返回False
。该函数将在一个失败后停止调用进一步的检查,或者它将返回所有检查的结果。
def all_or_none(checks, *args, **kwargs):
out = []
for check in checks:
rv = check(*args, **kwargs)
if not rv:
return None
out.append(rv)
return out
rv = all_or_none((check_a, check_b, check_c))
# rv is a list if all checks passed, otherwise None
if rv is not None:
return rv
def check_a(obj):
...
def check_b(obj):
...
# pass arguments to each check, useful for writing reusable checks
rv = all_or_none((check_a, check_b), obj=my_object)
答案 2 :(得分:6)
使用assignments as expressions的其他语言,您可以使用
if (a = check_a()) and (b = check_b()) and (c = check_c()):
但Python不是这样的语言。不过,我们可以绕过限制并模仿这种行为:
result = []
def put(value):
result.append(value)
return value
if put(check_a()) and put(check_b()) and put(check_c()):
# if you need them as variables, you could do
# (a, b, c) = result
# but you just want
return tuple(result)
这可能会使变量和函数调用之间的连接松散一些,所以如果你想用变量做很多单独的事情,而不是按照它们放入的顺序使用result
元素列表,我宁愿避免这种方法。不过,它可能比某些循环更快更短。</ p>
答案 3 :(得分:3)
您可以使用列表或OrderedDict,使用for循环可以起到模拟短路的作用。
from collections import OrderedDict
def check_a():
return "A"
def check_b():
return "B"
def check_c():
return "C"
def check_d():
return False
def method1(*args):
results = []
for i, f in enumerate(args):
value = f()
results.append(value)
if not value:
return None
return results
def method2(*args):
results = OrderedDict()
for f in args:
results[f.__name__] = result = f()
if not result:
return None
return results
# Case 1, it should return check_a, check_b, check_c
for m in [method1, method2]:
print(m(check_a, check_b, check_c))
# Case 1, it should return None
for m in [method1, method2]:
print(m(check_a, check_b, check_d, check_c))
答案 4 :(得分:2)
有很多方法可以做到这一点!这是另一个。
您可以使用生成器表达式来推迟函数的执行。然后,您可以使用itertools.takewhile
来实现短路逻辑,方法是使用生成器中的项目,直到其中一个为假。
from itertools import takewhile
functions = (check_a, check_b, check_c)
generator = (f() for f in functions)
results = tuple(takewhile(bool, generator))
if len(results) == len(functions):
return results
答案 5 :(得分:2)
解决这个问题的另一种方法是使用生成器,因为生成器使用惰性求值。首先将所有支票放入发电机:
def checks():
yield check_a()
yield check_b()
yield check_c()
现在,您可以通过将其转换为列表来强制评估所有内容:
list(checks())
但是标准的all函数对从checks()返回的迭代器进行了正确的快捷方式评估,并返回所有元素是否真实:
all(checks())
最后,如果您希望成功检查的结果达到失败,您可以使用itertools.takewhile仅执行第一次运行的truthy值。由于takewhile的结果本身是懒惰的,你需要将它转换为列表以在REPL中查看结果:
from itertools import takewhile
takewhile(lambda x: x, checks())
list(takewhile(lambda x: x, checks()))
答案 6 :(得分:1)
主要逻辑:
results = list(takewhile(lambda x: x, map(lambda x: x(), function_list)))
if len(results) == len(function_list):
return results
如果你查看api的所有方法,如http://www.scala-lang.org/api/2.11.7/#scala.collection.immutable.List和搜索/实现python等价物,你可以学到很多关于集合转换的知识
逻辑与设置和替代方案:
import sys
if sys.version_info.major == 2:
from collections import imap
map = imap
def test(bool):
def inner():
print(bool)
return bool
return inner
def function_for_return():
function_list = [test(True),test(True),test(False),test(True)]
from itertools import takewhile
print("results:")
results = list(takewhile(lambda x:x,map(lambda x:x(),function_list)))
if len(results) == len(function_list):
return results
print(results)
#personally i prefer another syntax:
class Iterator(object):
def __init__(self,iterable):
self.iterator = iter(iterable)
def __next__(self):
return next(self.iterator)
def __iter__(self):
return self
def map(self,f):
return Iterator(map(f,self.iterator))
def takewhile(self,f):
return Iterator(takewhile(f,self.iterator))
print("results2:")
results2 = list(
Iterator(function_list)
.map(lambda x:x())
.takewhile(lambda x:x)
)
print(results2)
print("with additional information")
function_list2 = [(test(True),"a"),(test(True),"b"),(test(False),"c"),(test(True),"d")]
results3 = list(
Iterator(function_list2)
.map(lambda x:(x[0](),x[1]))
.takewhile(lambda x:x[0])
)
print(results3)
function_for_return()
答案 7 :(得分:1)
灵活的短路最好用异常来完成。对于一个非常简单的原型,您甚至可以断言每个检查结果:
try:
a = check_a()
assert a
b = check_b()
assert b
c = check_c()
assert c
return a, b, c
except AssertionException as e:
return None
您应该提出自定义异常。您可以以任意嵌套方式更改check_X函数以引发自己的异常。或者你可以包装或修饰你的check_X函数,以便在错误的返回值上引发错误。
简而言之,异常处理非常灵活,正是您所寻找的,不要害怕使用它。如果您在某处学习了异常处理不用于您自己的流控制,那么这不适用于python。自由使用异常处理被认为是pythonic,如EAFP。
答案 8 :(得分:1)
如果您不需要在运行时使用任意数量的表达式(可能包含在lambdas中),您可以将代码直接扩展为此模式:
def f ():
try:
return (<a> or jump(),
<b> or jump(),
<c> or jump())
except NonLocalExit:
return None
这些定义适用的地方:
class NonLocalExit(Exception):
pass
def jump():
raise NonLocalExit()
答案 9 :(得分:1)
您在回答中提到了“短路”,这可以通过“或”语句完成。最佳答案基本上做同样的事情,但如果有人想更多地了解这种行为,你可以这样做;
class Container(object):
def __init__(self):
self.values = []
def check_and_cache(self, value, checking_function):
value_true = checking_function(value)
if value_true:
self.values.append(value)
return True
c = Container()
if not c.check_and_cache(a, check_a) or not c.check_and_cache(b, check_b) or not c.check_and_cache(c, check_c):
print 'done'
return tuple(c.values)
如果检查失败,if 语句的“not .. or”设置将导致“True”,因此整个 if 语句通过而不评估剩余值。
答案 10 :(得分:0)
由于我无法评论“wim”:作为嘉宾的答案,我只会添加一个额外的答案。 由于你需要一个元组,你应该在列表中收集结果,然后转换为元组。
def short_eval(*checks):
result = []
for check in checks:
checked = check()
if not checked:
break
result.append(checked)
return tuple(result)
# Example
wished = short_eval(check_a, check_b, check_c)
答案 11 :(得分:0)
您可以尝试使用@lazy_function
中的lazy_python
装饰器
package。用法示例:
from lazy import lazy_function, strict
@lazy_function
def check(a, b):
strict(print('Call: {} {}'.format(a, b)))
if a + b > a * b:
return '{}, {}'.format(a, b)
a = check(-1, -2)
b = check(1, 2)
c = check(-1, 2)
print('First condition')
if c and a and b: print('Ok: {}'.format((a, b)))
print('Second condition')
if c and b: print('Ok: {}'.format((c, b)))
# Output:
# First condition
# Call: -1 2
# Call: -1 -2
# Second condition
# Call: 1 2
# Ok: ('-1, 2', '1, 2')
答案 12 :(得分:0)
这类似于Bergi的答案,但我认为答案忽略了想要单独的函数(check_a,check_b,check_c):
list1 = []
def check_a():
condition = True
a = 1
if (condition):
list1.append(a)
print ("checking a")
return True
else:
return False
def check_b():
condition = False
b = 2
if (condition):
list1.append(b)
print ("checking b")
return True
else:
return False
def check_c():
condition = True
c = 3
if (condition):
list1.append(c)
print ("checking c")
return True
else:
return False
if check_a() and check_b() and check_c():
# won't get here
tuple1 = tuple(list1)
print (tuple1)
# output is:
# checking a
# (1,)
或者,如果您不想使用全局列表,请将本地列表的引用传递给每个函数。
答案 13 :(得分:0)
如果主要提出异议
如果检查很多,这很麻烦:
if a:
b = check_b()
if b:
c = check_c()
if c:
return a, b, c
一个相当不错的模式是扭转条件并提前返回
if not a:
return # None, or some value, or however you want to handle this
b = check_b()
if not b:
return
c = check_c()
if not c:
return
# ok, they were all truthy
return a, b, c