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

enr*_*cis 28 python syntax

有没有更好的方法在python中编写这段代码?

result = slow_function()
if result:
    return result
[...]
Run Code Online (Sandbox Code Playgroud)

该函数slow_function可以返回一个值,或者None它很慢,所以这是不可行的:

if slow_function():
    return slow_function()
Run Code Online (Sandbox Code Playgroud)

第一种方式没有任何问题,但使用临时变量似乎对python来说太过分了.

当您解决在使用递归调用过的问题.此代码是非常有用的f,并与当地的假设,比如你从列表中选择一个项目,然后检查是否有一个可行的解决方案,否则,你必须选择另外一个.就像是:

def f(n):
    for x in xrange(n):
        result = slow_function(x):
        if result:
            return result
        [...]
Run Code Online (Sandbox Code Playgroud)

不是更好的东西,如:

def f(n):
    for x in xrange(n):
        return slow_function(x) if is not None
Run Code Online (Sandbox Code Playgroud)

这可以扩展到检查任何类型的值.如果声明,它将是一个易于阅读的返回.


代码爱好者的附加示例

想象一下,你有一个数字列表列表:

lists = [[1,2,3],[4,5],[6,7,8],[9,10],...]
Run Code Online (Sandbox Code Playgroud)

并且您希望为每个列表选择一个项目,以便在选择中最多有一个偶数.可能有很多列表,所以尝试每个组合都会浪费,因为你已经可以告诉你,如果你开始选择[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
Run Code Online (Sandbox Code Playgroud)

这样的语法不会更好吗:

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
Run Code Online (Sandbox Code Playgroud)

到目前为止,我所做的最好的事情是在可行解决方案的生成器中转换函数:

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
Run Code Online (Sandbox Code Playgroud)

小智 14

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

  1. 你可以只返回函数的结果None:

    return slow_function()
    
    Run Code Online (Sandbox Code Playgroud)

    在这种情况下,您依赖于调用者知道如何处理该None值,并且实际上只是改变您的逻辑.

  2. 如果您有一个默认值而不是None,则可以执行以下操作:

    return slow_function() or default
    
    Run Code Online (Sandbox Code Playgroud)

    在上面,如果slow_functionNone(这是"假")它将返回后一个值,否则,如果slow_function返回"truthy"值,它将返回该值.当心,如果slow_function可以返回其他"falsy"的价值观,像False,[]或0,这些将被忽略.

  3. 或者,有时您所拥有的是完全有效的代码.您想要与值进行比较,如果是值,则返回它.你所拥有的代码在它的作用中是显而易见的,有时这比代码的"聪明"更重要.

按照意见,如果你的代码必须继续运行,如果该值None,则最明显的方式来做到这一点是它存储为一个临时的值.但是,这并不是一件坏事,因为它得很干净.

  • 计算一个值并将其存储为结果
  • 如果有有效结果,请将其返回.
  • 否则,继续做事以获得更好的结果.

更好的通常是非常主观的,我无法从计算的角度看到任何明显的方法来改进这一点,并且正如所写,它是非常人性化的,这是一个明显的优势.其他解决方案可能更短或更聪明,但人类可读性通常是代码的优势.

  • 正如他在评论中所说,只有当函数返回的内容不同于"None"时,他才想返回,否则函数的执行必须继续. (2认同)

Cla*_*diu 9

您的最新评论可能会让您更清楚自己想要做什么:

想象一下,你传递fa列表并选择一个项目,然后调用自己通过列表而没有项目,依此类推,直到你没有更多的项目.您检查解决方案是否可行,如果可行,您将返回解决方案,这需要一直通过调用堆栈,否则返回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
Run Code Online (Sandbox Code Playgroud)

现在它看起来像这样:

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
Run Code Online (Sandbox Code Playgroud)

好处是:

  • 您生成所有答案而不仅仅是第一个答案,但如果您仍然只想要一个答案,那么您可以获得第一个结果.
  • 在使用返回值进行错误检查和答案之前.现在你只有在得到答案时才会屈服.


Cla*_*diu 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])
Run Code Online (Sandbox Code Playgroud)

输出是:

Checking 2...
Checking 3...
Checking 4...
Checking 5...
1000
Run Code Online (Sandbox Code Playgroud)

诀窍是捎带异常处理,因为那些跨函数调用传播.如果你喜欢它,这是实现:

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
Run Code Online (Sandbox Code Playgroud)

  • @LegoStormtroopr:我没有说它更好.我的回答甚至始于**"我不会说这更好."**但是想出一种实际完成OP想要的方法很有趣.有时将语言弯曲到你的意志是一项有趣的练习.除了这个模式并不那么疯狂,它的灵感来自Twisted的`defer.returnValue()`,而且多个函数显然是可重用的. (7认同)
  • 只要我们理解我会愚蠢地在生产中部署它,就会对新颖性赞不绝口.:-) (4认同)