是否有更好的方法在python中编写此代码?
result = slow_function()
if result:
return result
[...]
函数slow_function
可以返回一个值或None
并且它很慢,所以这是不可行的:
if slow_function():
return slow_function()
第一种方式没有任何问题,但使用临时变量对于python来说似乎有些过分。
当您使用f
上的递归调用和本地假设解决问题时,此代码非常有用,例如,您从列表中选择一个项目,然后检查是否有可行的解决方案,否则您有选择另一个。类似的东西:
def f(n):
for x in xrange(n):
result = slow_function(x):
if result:
return result
[...]
像下面这样更具惯用性的东西不会更好:
def f(n):
for x in xrange(n):
return slow_function(x) if is not None
这可以扩展到检查任何类型的值。如果语句,这将是一个易于阅读的返回。
想象一下,你有一个数字列表列表:
lists = [[1,2,3],[4,5],[6,7,8],[9,10],...]
并且您希望为每个列表选择一个项目,以便在选择中最多只有一个偶数。可能会有很多列表,所以尝试每个组合都会浪费,因为你已经知道如果你开始选择[1,2,4,...]就没有可行的解决方案。
def check(selected):
even_numbers = filter(lambda n: (n % 2) == 0, selected)
return len(even_numbers) < 2
def f(lists, selected=[]):
if not lists:
return selected
for n in lists[0]:
if check(selected + [n]):
result = f(lists[1:], selected + [n])
if result:
return result
这样的语法不会更好:
def f(lists, selected=[]):
return selected if not lists
for n in lists[0]:
if check(selected + [n]):
return f(lists[1:], selected + [n]) if is not None
到目前为止,我所做的最好的事情是在可行解决方案的生成器中转换函数:
def f(lists, selected=[]):
if not lists:
yield selected
else:
for n in lists[0]:
if check(selected + [n]):
for solution in f(lists[1:], selected + [n]):
yield solution
答案 0 :(得分:14)
在不知道您可能想要返回的其他内容的情况下,有几个选项。
您只需返回函数None
的结果:
return slow_function()
在此,您依赖于调用者知道如何处理None
值,并且实际上只是转移到逻辑的位置。
如果您要返回默认值而不是None,则可以执行以下操作:
return slow_function() or default
如上所述,如果slow_function
为None
(即“假”),则会返回后一个值,否则,如果slow_function
返回“truthy”值,则返回那。请注意,如果slow_function
可以返回其他“虚假”值,例如False
,[]
或0,则会忽略这些值。
或者,有时你所拥有的是完全有效的代码。您想要与值进行比较,如果是值,则返回它。你所拥有的代码很明显,有时它比代码的“聪明”更重要。
根据评论,如果您的代码必须继续运行,如果值为None
,则最明显的方式将其存储为临时值。但是,这并不是一件坏事,因为它干净地读取。
更好通常是非常主观的,我无法从计算的角度看到任何明显的方法来改进这一点,而且正如所写,它是非常人性化的,这是一个明显的优势。其他解决方案可能更短或更聪明,但人类可读性往往是代码的优势。
答案 1 :(得分:8)
您的latest comment可能会让您更清楚自己想做什么:
想象一下,你传递一个列表并选择一个项目,然后调用自己通过列表而没有项目,依此类推,直到你没有更多的项目。您检查解决方案是否可行,如果可行,您将返回解决方案,这需要一直通过调用堆栈,否则返回None。通过这种方式,您将以拓扑顺序探索所有问题,但是当您知道先前选择的项目将无法创建可行解决方案时,您也可以跳过检查。
也许您可以尝试使用yield
代替return
。也就是说,您的递归函数不会生成一个解决方案,但会产生所有可能的解决方案。如果没有一个具体的例子,我无法确定你究竟在做什么,但在此之前说:
def solve(args, result_so_far):
if not slow_check_is_feasible(result_so_far):
#dead-end
return None
if not args:
#valid and done
return result_so_far
for i, item in enumerate(args):
#pass list without args - slow
new_args = args[:]
del new_args[i]
result = solve(new_args, accumulate_result(result_so_far, item)
if result is not None:
#found it, we are done
return result
#otherwise keep going
现在看起来像这样:
def solve_all(args, result_so_far):
if not slow_check_is_feasible(result_so_far):
#dead-end
return
if not args:
#yield since result was good
yield result_so_far
return
for i, item in enumerate(args):
#pass list without args - slow
new_args = args[:]
del new_args[i]
for result in solve(new_args, accumulate_result(result_so_far, item):
yield result
好处是:
答案 2 :(得分:7)
基本上你想要评估一个表达式,然后使用它两次而不将它绑定到局部变量。由于我们没有匿名变量,唯一的方法就是将它传递给函数。幸运的是,当前函数返回的控制流程不受它调用的函数的控制......但是,异常会传播到调用堆栈中。
我不会说这更好,但你可以滥用例外来获得你想要的东西。这应该永远不会被使用,而且更多是好奇心的练习。结果最终看起来像这样(注意装饰器的使用):
def slow_function(x):
if x % 5 == 0:
return x * 200
@if_returner
def foobme(l):
for i in l:
print "Checking %s..." % (i,)
return_if(slow_function(i))
print foobme([2, 3, 4, 5, 6])
输出是:
Checking 2...
Checking 3...
Checking 4...
Checking 5...
1000
诀窍是捎带异常处理,因为它们跨函数调用传播。如果你喜欢它,这是实现:
class ReturnExc(Exception):
def __init__(self, val):
self.val = val
def return_if(val):
if val is not None:
raise ReturnExc(val)
def if_returner(f):
def wrapped(*args, **kwargs):
try:
return f(*args, **kwargs)
except ReturnExc, e:
return e.val
return wrapped
答案 3 :(得分:2)
对于slow_function
在循环上运行的问题,生成器表达式似乎要走了。在Python 3中,这里的一切都是懒惰的,所以你可以免费获得过滤器:
f = filter(slow_function(x) for x in range(...))
在Python 2中你只需要itertools:
from itertools import ifilter
f = ifilter(slow_function(x) for x in xrange(...))
每次迭代只会在您要求时进行。如果你需要在函数返回false的情况下继续操作,那么你需要False
作为哨兵,所以你的解决方案很好:
def f():
for x in xrange(...):
sentinel = slow_function(x)
if sentinel:
return sentinel
# continue processing
或者你也可以在这里使用生成器来保存变量:
from itertools import imap
def f():
for x in imap(slow_function, xrange(...)):
if x:
return x
# continue processing
答案 4 :(得分:2)
不是推荐,但你可以滥用列表理解并按照以下方式做点什么:
# Note: Doesn't work in python 3.
def func():
if [value for value in (slow_function(),) if value is not None]:
return value
# continue processing...
答案 5 :(得分:-3)
你写的内容看起来不错,但是如果你想避免多个return语句,你可以这样做:
def f():
result = slow_function()
if result is None:
[...]
result = [...]
return result