360

我敢肯定有一种更简单的方法可以做到这一点,而我只是没有想到。

我正在调用一堆返回列表的方法。该列表可能为空。如果列表非空,我想返回第一项;否则,我想返回 None。此代码有效:

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

在我看来,这样做应该有一个简单的单行成语,但对于我的生活,我想不出它。有没有?

编辑:

我在这里寻找单行表达式的原因不是我喜欢非常简洁的代码,而是因为我不得不编写很多这样的代码:

x = get_first_list()
if x:
    # do something with x[0]
    # inevitably forget the [0] part, and have a bug to fix
y = get_second_list()
if y:
    # do something with y[0]
    # inevitably forget the [0] part AGAIN, and have another bug to fix

我想做的事情当然可以通过一个函数来完成(并且可能会是):

def first_item(list_or_none):
    if list_or_none: return list_or_none[0]

x = first_item(get_first_list())
if x:
    # do something with x
y = first_item(get_second_list())
if y:
    # do something with y

我发布这个问题是因为我经常对 Python 中的简单表达式可以做什么感到惊讶,并且我认为如果有一个简单的表达式可以解决问题,那么编写一个函数是一件愚蠢的事情。但是看到这些答案,似乎函数简单的解决方案。

4

24 回答 24

286

Python 2.6+

next(iter(your_list), None)

如果your_list可以None

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

蟒蛇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
于 2008-12-13T23:31:55.753 回答
251

最好的方法是这样的:

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

您也可以在一行中完成,但程序员阅读起来要困难得多:

return (get_list()[:1] or [None])[0]
于 2008-12-12T20:12:27.513 回答
88
(get_list() or [None])[0]

那应该行得通。

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

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

于 2008-12-12T20:00:03.920 回答
38

最 Python 惯用的方法是在迭代器上使用 next() ,因为 list 是iterable。就像@JFSebastian 在 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+

于 2014-08-20T06:36:29.900 回答
13

如果您发现自己试图从列表理解中提取第一件事(或无),您可以切换到生成器来执行以下操作:

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

优点:如果 blah 不是可索引的,则可以使用。缺点:这是不熟悉的语法。不过,在 ipython 中修改和过滤内容时它很有用。

于 2016-02-29T14:43:44.513 回答
11

OP 的解决方案几乎就在那里,只有几件事可以使它更加 Pythonic。

一方面,不需要获取列表的长度。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 风味。

于 2008-12-12T22:41:48.133 回答
7

Python成语返回第一项或无?

最 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 中的新功能,赋值表达式

赋值表达式使用就地赋值运算符(非正式地称为海象运算符),:=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

这假定函数在这里结束,隐式返回Noneifget_list返回一个空列表。下面的显式代码是完全等价的:

for item in get_list():
    return item
return None

if some_list

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

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

切片or [None]并选择第零个索引

这也是投票率最高的答案:

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

切片是不必要的,它会在内存中创建一个额外的单项列表。以下应该更高效。解释一下,or如果第一个元素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

然后我们有以下巧妙的使用内建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它只被调用一次,你首先讨论了推荐的 Pythonic 解决方案:

l = get_list()
return l[0] if l else None
于 2015-07-18T04:33:11.960 回答
4

my_list[0] if len(my_list) else None

于 2019-09-16T14:28:24.870 回答
3
for item in get_list():
    return item
于 2008-12-12T21:48:57.867 回答
3

关于成语,有一个名为nth.

从 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 一般适用于任何可迭代对象,而不仅仅是列表。

于 2017-08-25T17:02:56.377 回答
2

坦率地说,我不认为有更好的成语:你是清晰和简洁的 - 不需要任何“更好”的东西。也许吧,但这真的是一个品味问题,你可以改变if len(list) > 0:-if list:一个空列表将始终评估为 False。

在相关的说明中,Python不是Perl(没有双关语!),您不必获得最酷的代码。
实际上,我在 Python 中看到的最糟糕的代码也非常酷 :-) 并且完全无法维护。

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

于 2008-12-12T23:14:19.800 回答
1

出于好奇,我对其中两个解决方案进行了计时。使用 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

这些是我得到的时间:

空列表:
  index_first_item:
    0.748353 秒
    0.741086 秒
    0.741191 秒
    0.743543 秒平均
  return_first_item:
    0.785511 秒
    0.822178 秒
    0.782846 秒
    0.796845 秒平均
完整列表:
  index_first_item:
    0.762618 秒
    0.788040 秒
    0.786849 秒
    平均 0.779169 秒
  return_first_item:
    0.802735 秒
    0.878706 秒
    0.808781 秒
    平均 0.830074 秒
混合列表:
  index_first_item:
    0.791129 秒
    0.743526 秒
    0.744441 秒
    0.759699 秒平均
  return_first_item:
    0.784801 秒
    0.785146 秒
    0.840193 秒
    0.803380 秒平均
于 2008-12-16T07:07:33.257 回答
0
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))

(尽可能避免重复)

于 2008-12-16T13:17:12.963 回答
0

这个怎么样:

(my_list and my_list[0]) or None

注意:这对于对象列表应该可以正常工作,但根据下面的评论,如果是数字或字符串列表,它可能会返回不正确的答案。

于 2015-06-28T07:57:26.543 回答
0

不知道这是多么pythonic,但直到库中有第一个函数,我将它包含在源代码中:

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

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

于 2020-03-18T08:14:55.827 回答
0

借用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)
于 2020-11-15T19:46:42.720 回答
-1
try:
    return a[0]
except IndexError:
    return None
于 2008-12-12T20:30:19.117 回答
-1

使用 and-or 技巧:

a = get_list()
return a and a[0] or None
于 2008-12-12T23:48:00.637 回答
-1

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

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

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

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

好处:

-一条线

- 你只需打get_list()一次电话

-容易明白

于 2014-02-04T18:37:07.803 回答
-1

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

就我个人而言,我发现尝试和除样式清洁器可以阅读

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
于 2015-07-20T07:00:51.813 回答
-2

您可以使用提取方法。换句话说,将该代码提取到您将调用的方法中。

我不会尝试更多地压缩它,一个衬里似乎比冗长的版本更难阅读。如果你使用提取方法,它是一个单行;)

于 2008-12-12T20:10:04.010 回答
-2

有几个人建议做这样的事情:

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

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

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

于 2008-12-13T00:35:13.630 回答
-3

不是与 C 风格的三元运算符等效的惯用 python

cond and true_expr or false_expr

IE。

list = get_list()
return list and list[0] or None
于 2008-12-12T21:55:53.703 回答
-3
if mylist != []:

       print(mylist[0])

   else:

       print(None)
于 2016-10-25T09:54:19.547 回答