返回None或元组并解压缩

时间:2009-08-13 22:03:19

标签: python return-value

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

$ 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

事实是,为了正确解压而没有麻烦,我要么抓住TypeError,要么像

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

这有点烦人。有没有一个技巧来改善这种情况(例如,如果没有foo返回(无,无),将第一个和第二个设置为无)或者对我所提出的案例的最佳设计策略提出建议? *变量可能?

11 个答案:

答案 0 :(得分:20)

嗯,你可以做......

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

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

答案 1 :(得分:20)

我没有看到返回有什么问题(无,无)。它比这里建议的解决方案更清晰,这些解决方案涉及代码中的更多更改。

将None自动拆分为2个变量也没有意义。

答案 2 :(得分:15)

我认为存在抽象的问题。

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

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

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

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

请注意,此问题不会以原始名称清晰显示;这也是为什么在功能和方法上给出好名字总是很重要的原因。

答案 3 :(得分:7)

我认为没有诀窍。您可以将调用代码简化为:

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

甚至:

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

其中first_default和second_default是您给第一个和第二个默认值的值。

答案 4 :(得分:3)

这个怎么样:

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

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

修改:为了清楚起见,唯一的变化是将return None替换为return (None,)*2。没有人想到这一点我感到非常惊讶。 (或者如果他们有,我想知道他们为什么不使用它。)

答案 5 :(得分:2)

您应该谨慎使用x or y解决方案。它们有效,但它们比原始规格更广泛。基本上,如果foo(True)返回空元组(),该怎么办?只要您知道可以将其视为(None, None),您就可以使用所提供的解决方案。

如果这是常见的情况,我可能会编写一个实用程序函数,如:

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

答案 6 :(得分:1)

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

答案 7 :(得分:1)

好的,我只会返回(None,None),但只要我们处于whacko-land(嘿),这里就是使用元组子类的方法。在else的情况下,你不返回None,而是返回一个空容器,这似乎符合事物的精神。容器的“iterator”在空时解包无值。无论如何都要演示迭代器协议......

使用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

输出是理想的:

1 2
None None

答案 8 :(得分:1)

在控制方法foo时,可以用来完全避免该问题的一种机制是更改原型以提供默认值。如果要包装状态但不能保证存在特定的元组值,则此方法有效。

# self.r is for example, a redis cache

# This method is like foo - 
# it has trouble when you unpack a json serialized tuple
def __getitem__(self, key):
    val = self.r.get(key)
    if val is None:
        return None
    return json.loads(val)

# But this method allows the caller to 
# specify their own default value whether it be
# (None, None) or an entire object
def get(self, key, default):
    val = self.r.get(key)
    if val is None:
        return default
    return json.loads(val)

答案 9 :(得分:0)

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

返回None或返回一个对象。

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

像这样:

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

然后:

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

您可以访问以下结果:

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

答案 10 :(得分:0)

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

JSON_STRING='{"bucketname":"'$BUCKET_NAME'","objectname":"'$OBJECT_NAME'","targetlocation":"'$TARGET_LOCATION'"}'

但是,如果要跳过返回first, second = foo(False) or (first_default, second_default) 的情况,从Python 3.8开始,您可以使用 walrus运算符(即assignment expressions)-另请注意简化的None

foo

您可以使用def foo(flag): return (1, 2) if flag else None if values := Foo(False): (first, second) = values 分支来分配比上一个else选项差的默认值。

可悲的是,海象运算符does not support unparenthesized tuples使得与之相比,它仅是单行增益:

or