python 返回 None 或元组并解包

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1274875/
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-11-03 21:50:24  来源:igfitidea点击:

Returning None or a tuple and unpacking

pythonreturn-value

提问by Stefano Borini

I am always annoyed by this fact:

我总是对这个事实感到恼火:

$ cat foo.py
def foo(flag):
    if flag:
        return (1,2)
    else:
        return None

first, second = foo(True)
first, second = foo(False)

$ python foo.py
Traceback (most recent call last):
  File "foo.py", line 8, in <module>
    first, second = foo(False)
TypeError: 'NoneType' object is not iterable

The fact is that in order to correctly unpack without troubles I have either to catch the TypeError or to have something like

事实是,为了正确解包而不会遇到麻烦,我必须捕获 TypeError 或具有类似的东西

values = foo(False)
if values is not None:
    first, second = values

Which is kind of annoying. Is there a trick to improve this situation (e.g. to so set both first and second to None without having foo returning (None, None)) or a suggestion about the best design strategy for cases like the one I present ? *variables maybe ?

这有点烦人。是否有改善这种情况的技巧(例如,将 first 和 second 都设置为 None 而没有 foo 返回(None,None))或关于我提出的案例的最佳设计策略的建议?*可能是变量?

采纳答案by Amber

Well, you could do...

嗯,你可以这样做...

first,second = foo(True) or (None,None)
first,second = foo(False) or (None,None)

but as far as I know there's no simpler way to expand None to fill in the entirety of a tuple.

但据我所知,没有更简单的方法可以扩展 None 来填充整个元组。

回答by Unknown

I don't see what is wrong with returning (None,None). It is much cleaner than the solutions suggested here which involve far more changes in your code.

我不明白返回(无,无)有什么问题。它比此处建议的解决方案更简洁,后者涉及对代码进行更多更改。

It also doesn't make sense that you want None to automagically be split into 2 variables.

您希望 None 自动分成 2 个变量也是没有意义的。

回答by rob

I think there is a problem of abstraction.

我认为存在抽象问题。

A function should maintain some level of abstraction, that helps in reducing complexity of the code.
In this case, either the function is not maintaining the right abstraction, either the caller is not respecting it.

一个函数应该保持一定程度的抽象,这有助于降低代码的复杂性。
在这种情况下,要么函数没有维护正确的抽象,要么调用者不尊重它。

The function could have been something like get_point2d(); in this case, the level of the abstraction is on the tuple, and therefore returning None would be a good way to signal some particular case (e.g. non-existing entity). The error in this case would be to expect two items, while actually the only thing you know is that the function returns one object (with information related to a 2d point).

该功能可能是这样的get_point2d();在这种情况下,抽象级别在元组上,因此返回 None 将是表示某些特定情况(例如不存在的实体)的好方法。在这种情况下的错误是期望两个项目,而实际上您唯一知道的是该函数返回一个对象(具有与 2d 点相关的信息)。

But it could also have been something like get_two_values_from_db(); in this case the abstraction would be broken by returning None, because the function (as the name suggest) should return two values and not one!

但它也可能是这样的get_two_values_from_db();在这种情况下,抽象将通过返回 None 来破坏,因为函数(顾名思义)应该返回两个值而不是一个

Either way, the main goal of using a function - reducing complexity - is, at least partially, lost.

无论哪种方式,使用函数的主要目标——降低复杂性——至少部分地丢失了。

Note that this issue would not appear clearly with the original name; that's also why it is always important to give good names to function and methods.

请注意,此问题不会与原始名称一起清楚显示;这也是为什么给函数和方法起个好名字总是很重要的原因。

回答by Ned Batchelder

I don't think there's a trick. You can simplify your calling code to:

我不认为有什么窍门。您可以将调用代码简化为:

values = foo(False)
if values:
    first, second = values

or even:

甚至:

values = foo(False)
first, second = values or (first_default, second_default)

where first_default and second_default are values you'd give to first and second as defaults.

其中 first_default 和 second_default 是您提供给 first 和 second 作为默认值的值。

回答by Brian C. Wells

How about this:

这个怎么样:

$ cat foo.py 
def foo(flag):
    if flag:
        return (1,2)
    else:
        return (None,)*2

first, second = foo(True)
first, second = foo(False)

Edit:Just to be clear, the only change is to replace return Nonewith return (None,)*2. I am extremely surprised that no one else has thought of this. (Or if they have, I would like to know why they didn't use it.)

编辑:要清楚,唯一的变化是替换return Nonereturn (None,)*2. 我非常惊讶没有人想到这一点。(或者如果他们有,我想知道他们为什么不使用它。)

回答by ars

You should be careful with the x or ystyle of solution. They work, but they're a bit broader than your original specification. Essentially, what if foo(True)returns an empty tuple ()? As long as you know that it's OK to treat that as (None, None), you're good with the solutions provided.

您应该注意x or y解决方案的风格。它们可以工作,但比您的原始规格要宽一些。本质上,如果foo(True)返回一个空元组()怎么办?只要您知道可以将其视为(None, None),您就对所提供的解决方案感到满意。

If this were a common scenario, I'd probably write a utility function like:

如果这是一个常见的场景,我可能会编写一个实用函数,如:

# needs a better name! :)
def to_tup(t):
    return t if t is not None else (None, None)

first, second = to_tup(foo(True))
first, second = to_tup(foo(False))

回答by hughdbrown

def foo(flag):
    return ((1,2) if flag else (None, None))

回答by Anon

OK, I would just return (None, None), but as long as we are in whacko-land (heh), here is a way using a subclass of tuple. In the else case, you don't return None, but instead return an empty container, which seems to be in the spirit of things. The container's "iterator" unpacks None values when empty. Demonstrates the iterator protocol anyway...

好的,我只想返回 (None, None),但只要我们在 whacko-land (heh),这里有一种使用元组子类的方法。在 else 情况下,您不返回 None ,而是返回一个空容器,这似乎符合事物的精神。容器的“迭代器”在为空时解压缩 None 值。无论如何演示迭代器协议......

Tested using v2.5.2:

使用 v2.5.2 测试:

class Tuple(tuple):
    def __iter__(self):
        if self:
            # If Tuple has contents, return normal tuple iterator...
            return super(Tuple, self).__iter__()
        else:
            # Else return a bogus iterator that returns None twice...
            class Nonerizer(object):
                def __init__(self):
                    self.x=0
                def __iter__(self):
                    return self
                def next(self):
                    if self.x < 2:
                        self.x += 1
                        return None
                    else:
                        raise StopIteration
            return Nonerizer()


def foo(flag):
    if flag:
        return Tuple((1,2))
    else:
        return Tuple()  # It's not None, but it's an empty container.

first, second = foo(True)
print first, second
first, second = foo(False)
print first, second

Output is the desired:

输出是所需的:

1 2
None None

回答by Diego

I found a solution for this problem:

我找到了解决这个问题的方法:

Return None or return an object.

返回 None 或返回一个对象。

However you don't want to have to write a class just to return an object. For this, you can use a named tuple

但是,您不想为了返回一个对象而编写一个类。为此,您可以使用命名元组

Like this:

像这样:

from collections import namedtuple
def foo(flag):
if flag:
   return None
else:
    MyResult = namedtuple('MyResult',['a','b','c']
    return MyResult._make([1,2,3])

And then:

接着:

result = foo(True) # result = True
result = foo(False) # result = MyResult(a=1, b=2, c=3)

And you have access to the results like this:

您可以访问这样的结果:

print result.a # 1
print result.b # 2
print result.c # 3

回答by jnovo

Over 10 years later, if you want to use default values I don't think there is a better way than the one already provided:

10 多年后,如果您想使用默认值,我认为没有比已经提供的方法更好的方法了:

first, second = foo(False) or (first_default, second_default)

However, if you want to skip the case when Noneis returned, starting from Python 3.8 you can use the walrus operator(ie. assignment expressions) - also note the simplified foo:

但是,如果您想跳过None返回的情况,从 Python 3.8 开始,您可以使用walrus 运算符(即赋值表达式) - 还请注意简化foo

def foo(flag):
    return (1, 2) if flag else None

if values := Foo(False):
    (first, second) = values

You could use an elsebranch to assign default values that's worse than the previous oroption.

您可以使用else分支来分配比前一个or选项差的默认值。

Sadly, the walrus operator does not support unparenthesized tuplesso it is just a one line gain compared to:

可悲的是,海象运算符不支持无括号的元组,因此与以下相比,它只是一行增益:

values = foo(False)
if values:
    first, second = values