我总是对这个事实感到恼火:
$ 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返回(无,无),将第一个和第二个设置为无)或者对我所提出的案例的最佳设计策略提出建议? *变量可能?
答案 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