代码之家  ›  专栏  ›  技术社区  ›  Stefano Borini

返回none或tuple并解包

  •  20
  • Stefano Borini  · 技术社区  · 16 年前

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

    $ 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返回(无,无)的情况下,将first和second都设置为none),或者对我所呈现的这种情况提出最佳设计策略的建议?*变量可能?

    9 回复  |  直到 16 年前
        1
  •  20
  •   Amber    16 年前

    好吧,你可以…

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

    但据我所知,没有更简单的方法可以展开“无”来填充元组的全部内容。

        2
  •  19
  •   Unknown    16 年前

    我不知道退货有什么问题(没有,没有)。它比这里建议的解决方案要干净得多,这些解决方案涉及到代码中更多的更改。

    你也不希望一个变量自动拆分成两个变量。

        3
  •  14
  •   rob    16 年前

    我想有个问题 抽象化 .

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

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

    但也可能是 get_two_values_from_db() ;在这种情况下,抽象将被破坏,因为函数(顾名思义)没有返回。 应返回两个值而不是一个值 你说什么?

    不管怎样,使用一个函数的主要目标——减少复杂性——至少是部分地丢失了。

    请注意,这个问题不会以原始名称清晰地出现;这也是为什么给函数和方法命名总是很重要的原因。

        4
  •  7
  •   Ned Batchelder    16 年前

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

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

    甚至:

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

    其中第一个默认值和第二个默认值是作为默认值提供给第一个和第二个的值。

        5
  •  3
  •   Brian C. Wells    16 年前

    这个怎么样?

    $ 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 . 我非常惊讶没有人想到这一点。(或者如果他们有,我想知道他们为什么不使用它。)

        6
  •  2
  •   ars    16 年前

    你应该小心 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))
    
        7
  •  1
  •   hughdbrown    16 年前
    def foo(flag):
        return ((1,2) if flag else (None, None))
    
        8
  •  1
  •   Anon    16 年前

    好的,我只会返回(无,无),但只要我们在whacko-land(heh),这里有一种使用tuple子类的方法。在其他情况下,你不会一个也不返回,而是返回一个空容器,这似乎是在精神上的东西。容器的“迭代器”空时不解包任何值。无论如何演示迭代器协议…

    使用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
    
        9
  •  0
  •   Diego    8 年前

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

    不返回或返回对象。

    但是,您不希望只编写一个类来返回一个对象。为此,您可以使用 名元组

    这样地:

    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