返回第一项或None

我调用了一堆返回列表的方法。列表可能是空的。如果列表非空,我想返回第一项;否则,我想返回None。这段代码工作:

def main():
my_list = get_list()
if len(my_list) > 0:
return my_list[0]
return None

但是,在我看来,应该有一个简单的一行习语来做这件事。是吗?

382678 次浏览
(get_list() or [None])[0]

这应该有用。

顺便说一句,我没有使用变量list,因为这会覆盖内置的list()函数。

编辑:我之前有一个稍微简单,但错误的版本。

你可以使用提取方法。换句话说,将代码提取到一个方法中,然后调用该方法。

我不会试图进一步压缩它,一行代码似乎比冗长的版本更难阅读。如果你使用提取方法,它是一行;)

最好的方法是:

a = get_list()
return a[0] if a else None

你也可以在一行中完成,但这对程序员来说很难读懂:

return (get_list()[:1] or [None])[0]
try:
return a[0]
except IndexError:
return None
for item in get_list():
return item

不是python中等价于c风格的三元运算符

cond and true_expr or false_expr

ie。

list = get_list()
return list and list[0] or None

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风格。

坦率地说,我认为没有更好的成语了:your is clear and short -不需要什么“更好的”。也许吧,但这真的是一个品味问题,你可以用if list:来改变if len(list) > 0:——一个空列表的值总是为False。

在相关的说明中,Python是 Perl(没有双关的意思!),你不需要得到最酷的代码 实际上,我在Python中见过的最糟糕的代码,也是非常酷的:-),而且完全无法维护

顺便说一下,我在这里看到的大多数解决方案都不考虑list[0]计算为False(例如空字符串或零)-在这种情况下,它们都返回None而不是正确的元素。

使用and-or技巧:

a = get_list()
return a and a[0] or None

一些人建议这样做:

list = get_list()
return list and list[0] or None

这在许多情况下都有效,但它只在列表[0]不等于0、False或空字符串时才有效。如果列表[0]为0、False或空字符串,该方法将错误地返回None。

我在自己的代码中创建了这个错误太多次了!

Python 2.6 +

next(iter(your_list), None)

如果your_list可以是None:

next(iter(your_list or []), None)

Python 2.4

def get_first(iterable, default=None):
if iterable:
for item in iterable:
return item
return default

例子:

x = get_first(get_first_list())
if x:
...
y = get_first(get_second_list())
if y:
...

另一个选择是内联上面的函数:

for x in get_first_list() or []:
# process x
break # process at most one item
for y in get_second_list() or []:
# process y
break

为了避免break,你可以这样写:

for x in yield_first(get_first_list()):
x # process x
for y in yield_first(get_second_list()):
y # process y

地点:

def yield_first(iterable):
for item in iterable or []:
yield item
return

出于好奇,我对其中两个解决方案进行了计时。使用return语句提前结束for循环的解决方案在我的机器上使用Python 2.5.1的代价略高,我怀疑这与设置可迭代对象有关。

import random
import timeit


def index_first_item(some_list):
if some_list:
return some_list[0]




def return_first_item(some_list):
for item in some_list:
return item




empty_lists = []
for i in range(10000):
empty_lists.append([])


assert empty_lists[0] is not empty_lists[1]


full_lists = []
for i in range(10000):
full_lists.append(list([random.random() for i in range(10)]))


mixed_lists = empty_lists[:50000] + full_lists[:50000]
random.shuffle(mixed_lists)


if __name__ == '__main__':
ENV = 'import firstitem'
test_data = ('empty_lists', 'full_lists', 'mixed_lists')
funcs = ('index_first_item', 'return_first_item')
for data in test_data:
print "%s:" % data
for func in funcs:
t = timeit.Timer('firstitem.%s(firstitem.%s)' % (
func, data), ENV)
times = t.repeat()
avg_time = sum(times) / len(times)
print "  %s:" % func
for time in times:
print "    %f seconds" % time
print "    %f seconds avg." % avg_time

以下是我得到的时间:

empty_lists:
index_first_item:
0.748353 seconds
0.741086 seconds
0.741191 seconds
0.743543 seconds avg.
return_first_item:
0.785511 seconds
0.822178 seconds
0.782846 seconds
0.796845 seconds avg.
full_lists:
index_first_item:
0.762618 seconds
0.788040 seconds
0.786849 seconds
0.779169 seconds avg.
return_first_item:
0.802735 seconds
0.878706 seconds
0.808781 seconds
0.830074 seconds avg.
mixed_lists:
index_first_item:
0.791129 seconds
0.743526 seconds
0.744441 seconds
0.759699 seconds avg.
return_first_item:
0.784801 seconds
0.785146 seconds
0.840193 seconds
0.803380 seconds avg.
def head(iterable):
try:
return iter(iterable).next()
except StopIteration:
return None


print head(xrange(42, 1000)  # 42
print head([])               # None

顺便说一句:我会把你的一般程序流程重做成这样:

lists = [
["first", "list"],
["second", "list"],
["third", "list"]
]


def do_something(element):
if not element:
return
else:
# do something
pass


for li in lists:
do_something(head(li))

(尽可能避免重复)

可能不是最快的解决方案,但没有人提到这个选项:

dict(enumerate(get_list())).get(0)

如果get_list()可以返回None,你可以使用:

dict(enumerate(get_list() or [])).get(0)

优点:

一线

-你只需调用get_list()一次

-容易理解

最python惯用的方法是在迭代器上使用next(),因为list是可迭代的。就像@J.F。塞巴斯蒂安在2011年12月13日发表了这条评论。

next(iter(the_list), None)如果the_list为空则返回None。看到next() Python 2.6+

或者如果你确定the_list不是空的:

iter(the_list).next()参见iterator.next() Python 2.2+

这个怎么样:

(my_list and my_list[0]) or None

注意:这应该适用于对象列表,但它可能会返回错误的答案,在数字或字符串列表的情况下,每个下面的注释。

返回第一项还是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

文章提议对语言进行这一更改。他没有提到第一个公式,但基于他喜欢的其他公式,我认为他不会介意。

这里演示的其他方法,并附有解释

< h3 id = " - 073 y”> for < / h3 >

当我开始试图想出聪明的方法来做到这一点时,我想到的第二件事是:

for item in get_list():
return item

这假定函数在此结束,如果get_list返回一个空列表,则隐式返回None。下面的显式代码完全等价:

for item in get_list():
return item
return None
< h3 id = " if-some_list-lz4k " > if some_list < / h3 >

下面还提出了(我更正了不正确的变量名),它也使用隐式None。这比上面的方法更可取,因为它使用逻辑检查而不是可能不会发生的迭代。这应该更容易立即理解发生了什么。但如果我们是为了可读性和可维护性而写,我们还应该在结尾添加显式的return None:

some_list = get_list()
if some_list:
return some_list[0]

slice or [None] and select zero index

这也是投票最多的答案:

return (get_list()[:1] or [None])[0]

切片是不必要的,它会在内存中创建一个额外的单项列表。下面的代码应该性能更好。为了解释,如果布尔上下文中的第一个元素是Falseor将返回第二个元素,因此如果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
< h3 id = " next-gj43 " > next < / h3 >

然后,我们有以下内置nextiter的聪明使用

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

我的用例只是设置一个局部变量的值。

就我个人而言,我发现try and except风格更容易阅读

items = [10, 20]
try: first_item = items[0]
except IndexError: first_item = None
print first_item

而不是切片列表。

items = [10, 20]
first_item = (items[:1] or [None, ])[0]
print first_item

如果你发现自己试图从列表理解中提取第一件事(或None),你可以切换到生成器来这样做:

next((x for x in blah if cond), None)

正方:如果blah是不可索引的,它是有效的,反面:它是不熟悉的语法。不过,当在ipython中破解和过滤东西时,它很有用。

if mylist != []:


print(mylist[0])


else:


print(None)

关于习语,有一个名为nthitertools食谱

来自itertools食谱:

def nth(iterable, n, default=None):
"Returns the nth item or a default value"
return next(islice(iterable, n, None), default)

如果你想要一行程序,考虑安装一个库来实现这个配方,例如more_itertools:

import more_itertools as mit


mit.nth([3, 2, 1], 0)
# 3


mit.nth([], 0)                                             # default is `None`
# None

还有另一个只返回第一项的工具,称为more_itertools.first

mit.first([3, 2, 1])
# 3


mit.first([], default=None)
# None

这些itertools一般适用于任何可迭代对象,而不仅仅适用于列表。

my_list[0] if len(my_list) else None

不确定这是如何python化的,但在库中有第一个函数之前,我将其包含在源代码中:

first = lambda l, default=None: next(iter(l or []), default)

它只有一行(符合黑色),避免了依赖关系。

借用more_itertools.first_true代码产生一些可读的东西:

def first_true(iterable, default=None, pred=None):
return next(filter(pred, iterable), default)


def get_first_non_default(items_list, default=None):
return first_true(items_list, default, pred=lambda x: x!=default)