在 Python 中更好的“如果不是 None 则返回”

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19415614/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-19 13:42:02  来源:igfitidea点击:

Better "return if not None" in Python

pythonsyntax

提问by enrico.bacis

Is there a betterway to write this code in python?

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

result = slow_function()
if result:
    return result
[...]

The function slow_functioncan return a value or Noneand it's slow, so this is not feasible:

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

if slow_function():
    return slow_function()

There is nothing wrong with the first way, but using a temporary variable seems overkill for python.

第一种方式没有错,但是使用临时变量对 python 来说似乎有点过分。

This code is pretty useful when you are solving a problem using recursive calls over fand with local assumption, for example you select an item from a list and then check if there is a feasible solution, otherwise you have to choose another one. Something like:

当您使用递归调用f和本地假设解决问题时,此代码非常有用,例如,您从列表中选择一个项目,然后检查是否有可行的解决方案,否则您必须选择另一个。就像是:

def f(n):
    for x in xrange(n):
        result = slow_function(x):
        if result:
            return result
        [...]

Wouldn't it be better something more idiomatic like:

像这样更惯用的东西不是更好吗:

def f(n):
    for x in xrange(n):
        return slow_function(x) if is not None

This can be extended to check any kind of value. It would be an easy-to-read return ifstatement.

这可以扩展到检查任何类型的值。这将是一个易于阅读的return if语句。



Additional example for code lovers

代码爱好者的附加示例

Imagine you have a list of lists of numbers:

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

lists = [[1,2,3],[4,5],[6,7,8],[9,10],...]

and you want to select one item for each list such that there is at most one even number in the selection. There could be a lot of lists, so trying each combination would be wasteful since you can already tell that if you start selecting [1,2,4,...] there could be no feasible solutions.

并且您想为每个列表选择一个项目,以便选择中最多有一个偶数。可能有很多列表,所以尝试每个组合都是浪费,因为你已经知道如果你开始选择 [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

Wouldn't it be better a syntax like:

像这样的语法不是更好吗:

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

The best I've done so far is to turn the function in a generator of feasible solutions:

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

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

采纳答案by Claudiu

Your latest commentmaybe makes it clearer what you want to do:

您的最新评论可能会更清楚地说明您想要做什么:

Imagine that you pass f a list and it select an item, then calls itself passing the list without the item and so on until you have no more items. The you check if the solution is feasible, if it is feasible you'll return the solution and this needs to go all the way through the call stack, otherwise you return None. In this way you'll explore all the problems in a topological order but you can also skip checks when you know that the previous chosen items won't be able to create a feasible solution.

想象一下,您传递 fa list 并选择一个项目,然后调用自身传递没有该项目的列表,依此类推,直到您没有更多项目。您检查解决方案是否可行,如果可行,您将返回解决方案,这需要一直通过调用堆栈,否则返回 None。通过这种方式,您将按拓扑顺序探索所有问题,但当您知道之前选择的项目将无法创建可行的解决方案时,您也可以跳过检查。

Maybe you can try using yieldinstead of return. That is, your recursive function won't generate one solution, but will yield all possible solutions. Without a specific example I can't be sure what you're doing exactly, but say before it was like this:

也许您可以尝试使用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

Now it looks like this:

现在它看起来像这样:

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

The benefits are:

好处是:

  • You generate all answers instead of just the first one, but if you still only want one answer then you can just get the first result.
  • Before you used return values both for false checks and for answers. Now you're just only yielding when you have an answer.
  • 您生成所有答案,而不仅仅是第一个,但如果您仍然只想要一个答案,那么您可以获得第一个结果。
  • 在您将返回值用于错误检查和答案之前。现在,您只有在得到答案时才会屈服。

回答by enrico.bacis

Without knowing what else you might want to return there are a few options.

在不知道您还想返回什么的情况下,有几种选择。

  1. You could just return the result of the function, Noneor not:

    return slow_function()
    

    In this, you rely on the caller knowing what to do with the Nonevalue, and really just shift where your logic will be.

  2. If you have a default value to return instead of None, you can do this:

    return slow_function() or default
    

    In this above, if slow_functionis None(which is "falsy") it will return the latter value, otherwise, if slow_functionreturns a "truthy" value it will return that. Beware, if slow_functioncan return other "falsy" values, like False, []or 0, those will be ignored.

  3. Alternatively, sometimes what you have is perfectly valid code. You want to compare against a value, and if it is value, return it. The code you have is obvious in what it does, and sometimes that is more important than the "cleverness" of the code.

  1. 您可以只返回函数的结果,也可以不返回None

    return slow_function()
    

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

  2. 如果你有一个默认值要返回而不是 None,你可以这样做:

    return slow_function() or default
    

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

  3. 或者,有时您拥有的是完全有效的代码。您想与一个值进行比较,如果是值,则返回它。您拥有的代码在其功能方面是显而易见的,有时这比代码的“聪明”更重要。

As per the comments, if your code must continue running if the value is Nonethen the most obviousway to do it is store it as a temporary value. But, thats not a bad thing, as it readscleanly.

根据评论,如果您的代码必须继续运行,None那么最明显的方法是将其存储为临时值。但是,这不是一件坏事,因为它读起来很干净。

  • Compute a value and store it as the result
  • If there is a valid result, return it.
  • Otherwise, keep doing things to get a better result.
  • 计算一个值并将其存储为结果
  • 如果有一个有效的结果,则返回它。
  • 否则,继续做事情以获得更好的结果。

Betteris usually very subjective, and I can't see any obvious ways to improve this from a computation perspective, and as written it is very human readable which is a clear advantage. Other solutions may be shorter or cleverer, but human readability is often an over looked advantage for code.

Better通常是非常主观的,从计算的角度来看,我看不出有任何明显的方法可以改进这一点,并且正如所写的那样,它非常易于阅读,这是一个明显的优势。其他解决方案可能更短或更聪明,但人类可读性通常是代码的一个被忽视的优势。

回答by Claudiu

Essentially you want to evaluate an expression and then use it twice without binding it to a local variable. The only way to do that, since we don't have anonymous variables, is to pass it into a function. Fortunately, the control flow for whether the current function returns isn't controlled by the functions it calls... however, exceptions do propagate up the call stack.

本质上,您想要计算一个表达式,然后使用它两次而不将其绑定到局部变量。因为我们没有匿名变量,所以唯一的方法就是将它传递给一个函数。幸运的是,当前函数是否返回的控制流不受它调用的函数的控制……但是,异常确实会沿调用堆栈向上传播。

I wouldn't say this is better, but you could abuse exceptions to get what you want. This should never really be used and it's more an exercise in curiosity. The result would end up looking like this (note the use of the decorator):

我不会说这更好,但你可以滥用异常来获得你想要的。这永远不应该被真正使用,它更像是一种好奇心的练习。结果最终看起来像这样(注意装饰器的使用):

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])

Output is:

输出是:

Checking 2...
Checking 3...
Checking 4...
Checking 5...
1000

The trick is to piggy-back on exception handling, since those propagate across function calls. If you like it, here's the implementation:

诀窍是搭载异常处理,因为这些会在函数调用之间传播。如果你喜欢它,这是实现:

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

回答by David Ginsburg

What you have written looks fine, but if you wanted to avoid multiple return statements you could do something like this:

你写的看起来不错,但如果你想避免多个 return 语句,你可以这样做:

def f():
    result = slow_function()
    if result is None:
        [...]
        result = [...]
    return result

回答by kojiro

For the problem where slow_functionis operating over a loop, a generator expression would seem the way to go. In Python 3 everything here is lazy, so you get your filter for free:

对于在slow_function循环上操作的问题,生成器表达式似乎是要走的路。在 Python 3 中,这里的一切都是惰性的,因此您可以免费获得过滤器:

f = filter(slow_function(x) for x in range(...))

In Python 2 you just need itertools:

在 Python 2 中你只需要 itertools:

from itertools import ifilter

f = ifilter(slow_function(x) for x in xrange(...))

Each iteration will only take place when you ask for it. If you need to continue the operation if the function returns false, then you need the Falseness as a sentinel, so your solution is fine:

每次迭代只会在您要求时发生。如果您需要在函数返回 false 时继续操作,那么您需要将Falseness 作为哨兵,因此您的解决方案很好:

def f():
  for x in xrange(...):
    sentinel = slow_function(x)
    if sentinel:
      return sentinel
    # continue processing

or you can save yourself a variable by using a generator here, too:

或者你也可以在这里使用生成器来保存一个变量:

from itertools import imap

def f():
  for x in imap(slow_function, xrange(...)):
    if x:
      return x
    # continue processing

回答by martineau

Not really a recommendation, but you could abuse a list comprehension and do something along these lines:

不是真正的建议,但您可以滥用列表理解并按照以下方式做一些事情:

# 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...