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
Returning None or a tuple and unpacking
提问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 None
with 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 None
为return (None,)*2
. 我非常惊讶没有人想到这一点。(或者如果他们有,我想知道他们为什么不使用它。)
回答by ars
You should be careful with the x or y
style 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 None
is 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 else
branch to assign default values that's worse than the previous or
option.
您可以使用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