返回 None 或元组并解包

46 投票
11 回答
34480 浏览
提问于 2025-04-15 13:35

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

$ 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

这个事实是,为了正确地解包而不出问题,我要么得捕获类型错误,要么得有类似下面的东西:

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

这实在有点烦人。有没有什么技巧可以改善这种情况(比如说,能不能在不让 foo 返回 (None, None) 的情况下,把第一个和第二个都设置为 None)或者对我提到的这种情况有什么好的设计策略建议?*也许可以用变量?

11 个回答

23

我觉得这里有一个抽象的问题。

一个函数应该保持一定的抽象层次,这样可以帮助减少代码的复杂性。
在这种情况下,要么这个函数没有保持正确的抽象,要么调用这个函数的人没有遵守这个抽象。

这个函数可以是像get_point2d()这样的名字;在这种情况下,抽象层次是关于元组的,因此返回None可以很好地表示某种特定情况(比如说某个实体不存在)。在这种情况下,错误在于期待返回两个值,而实际上你只知道这个函数返回一个对象(与一个二维点相关的信息)。

但它也可以是像get_two_values_from_db()这样的名字;在这种情况下,返回None就打破了抽象,因为这个函数(从名字上看)应该返回两个值,而不是一个

无论如何,使用函数的主要目标——减少复杂性——在某种程度上是失去的。

注意,这个问题在原来的名字下并不明显;这也是为什么给函数和方法起个好名字总是很重要的原因。

34

我觉得返回 (None, None) 没什么问题。这比这里提到的那些解决方案要简单得多,因为后者需要对你的代码做很多改动。

而且,让 None 自动分成两个变量也不太合理。

43

好吧,你可以这样做...

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

不过据我所知,没有更简单的方法可以把 None 扩展成填满整个元组。

撰写回答