我调用了一堆返回列表的方法。列表可能是空的。如果列表非空,我想返回第一项;否则,我想返回None。这段代码工作:
def main():
my_list = get_list()
if len(my_list) > 0:
return my_list[0]
return None
但是,在我看来,应该有一个简单的一行习语来做这件事。是吗?
我调用了一堆返回列表的方法。列表可能是空的。如果列表非空,我想返回第一项;否则,我想返回None。这段代码工作:
def main():
my_list = get_list()
if len(my_list) > 0:
return my_list[0]
return None
但是,在我看来,应该有一个简单的一行习语来做这件事。是吗?
当前回答
如果你发现自己试图从列表理解中提取第一件事(或None),你可以切换到生成器来这样做:
next((x for x in blah if cond), None)
正方:如果blah是不可索引的,它是有效的,反面:它是不熟悉的语法。不过,当在ipython中破解和过滤东西时,它很有用。
其他回答
这个怎么样:
(my_list and my_list[0])或None
注意:这应该适用于对象列表,但它可能会返回错误的答案,在数字或字符串列表的情况下,每个下面的注释。
可能不是最快的解决方案,但没有人提到这个选项:
dict(enumerate(get_list())).get(0)
如果get_list()可以返回None,你可以使用:
dict(enumerate(get_list() or [])).get(0)
优点:
一线
-你只需要调用get_list()一次
-容易理解
返回第一项还是None?
最Pythonic的方法是得到最多赞的答案所展示的,这是我读到这个问题时想到的第一件事。下面是如何使用它,首先,如果可能为空的列表被传递到一个函数:
def get_first(l):
return l[0] if l else None
如果从get_list函数返回列表:
l = get_list()
return l[0] if l else None
赋值表达式:Python 3.8新增功能
赋值表达式使用就地赋值操作符(非正式地称为walrus操作符),:=,Python 3.8新增的操作符,允许我们就地执行检查和赋值,允许一行代码:
return l[0] if (l := get_list()) else None
作为一个长期的Python用户,这感觉像是我们试图在一行上做太多的事情——我觉得做假定相同的性能会是更好的风格:
if l := get_list():
return l[0]
return None
为了支持这一提法,Tim Peter在PEP中提出了对语言的这一改变。他没有提到第一个公式,但基于他喜欢的其他公式,我认为他不会介意。
这里演示了其他方法,并给出了解释
for
当我开始试图想出聪明的方法来做到这一点时,我想到的第二件事是:
for item in get_list():
return item
这假定函数在此结束,如果get_list返回空列表,则隐式返回None。下面的显式代码完全等价:
for item in get_list():
return item
return None
如果some_list
下面还提出了(我更正了不正确的变量名),它也使用隐式的None。这比上面的方法更可取,因为它使用逻辑检查而不是可能不会发生的迭代。这应该更容易立即理解发生了什么。但如果我们是为了可读性和可维护性而编写,我们还应该在末尾添加显式返回None:
some_list = get_list()
if some_list:
return some_list[0]
slice或[None]并选择第0个索引
这也是投票最多的答案:
return (get_list()[:1] or [None])[0]
切片是不必要的,它会在内存中创建一个额外的单项列表。下面的代码应该性能更好。为了解释,如果第一个元素在布尔上下文中为False,则返回第二个元素,因此如果get_list返回一个空列表,括号中包含的表达式将返回一个带有'None'的列表,然后由0索引访问:
return (get_list() or [None])[0]
下一个表达式使用的事实是,如果第一个在布尔上下文中为True,则返回第二项,并且由于它引用了my_list两次,因此它并不比三元表达式更好(并且技术上不是一行程序):
my_list = get_list()
return (my_list and my_list[0]) or None
next
然后,我们有以下聪明的使用内置的next和iter
return next(iter(get_list()), None)
为了解释,iter返回一个带有.next方法的迭代器。(。__next__在Python 3中。)然后内建的next调用.next方法,如果迭代器耗尽,则返回我们给出的默认值None。
冗余三元表达式(a if b else c)并绕回来
有人提出了以下观点,但相反的观点更可取,因为逻辑通常更容易从积极的角度来理解,而不是从消极的角度。因为get_list被调用了两次,除非结果以某种方式被记住,否则执行效果会很差:
return None if not get_list() else get_list()[0]
更好的逆:
return get_list()[0] if get_list() else None
更好的是,使用一个局部变量,这样get_list只被调用一次,并且您首先讨论了推荐的python解决方案:
l = get_list()
return l[0] if l else None
下面的代码涵盖了使用lambda的几个场景:
l1 = [1,2,3]
l2 = []
l3 = None
first_elem = lambda x: x[0] if x else None
print(first_elem(l1))
print(first_elem(l2))
print(first_elem(l3))
OP的解决方案已经差不多了,只是有几件事让它更python化。
首先,不需要知道列表的长度。Python中的空列表在if检查中求值为False。只要简单地说
if list:
此外,赋值给与保留字重叠的变量是一个非常糟糕的主意。list是Python中的保留字。
我们把它改成
some_list = get_list()
if some_list:
这里很多解决方案忽略的一个非常重要的一点是,所有Python函数/方法默认返回None。试试下面的方法。
def does_nothing():
pass
foo = does_nothing()
print foo
除非你需要返回None来提前终止一个函数,否则没有必要显式地返回None。非常简洁,只要返回第一个条目,如果它存在的话。
some_list = get_list()
if some_list:
return list[0]
最后,也许这是隐含的,但只是显式的(因为显式比隐式好),你不应该让你的函数从另一个函数获取列表;把它作为参数传入。最后的结果是
def get_first_item(some_list):
if some_list:
return list[0]
my_list = get_list()
first_item = get_first_item(my_list)
正如我所说的,OP几乎已经完成了,只需稍加修改就可以获得您正在寻找的Python风格。