更好"如果不是没有返回"在Python中

时间:2013-10-16 23:18:09

标签: python syntax

是否有更好的方法在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

6 个答案:

答案 0 :(得分:14)

在不知道您可能想要返回的其他内容的情况下,有几个选项。

  1. 您只需返回函数None的结果:

    return slow_function()
    

    在此,您依赖于调用者知道如何处理None值,并且实际上只是转移到逻辑的位置。

  2. 如果您要返回默认值而不是None,则可以执行以下操作:

    return slow_function() or default
    

    如上所述,如果slow_functionNone(即“假”),则会返回后一个值,否则,如果slow_function返回“truthy”值,则返回那。请注意,如果slow_function可以返回其他“虚假”值,例如False[]或0,则会忽略这些值。

  3. 或者,有时你所拥有的是完全有效的代码。您想要与值进行比较,如果是值,则返回它。你所拥有的代码很明显,有时它比代码的“聪明”更重要。

  4. 根据评论,如果您的代码必须继续运行,如果值为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