Python成语返回第一项或无


272

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

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

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中的简单表达式可以做什么感到惊讶,并且我认为如果有一个简单的表达式可以完成此任务,那么编写函数是一件很愚蠢的事情。但是看到这些答案,似乎函数简单的解决方案。


28
顺便说一句,在Python 2.6+上,您可以使用next(iter(your_list), None)而不是first_item(your_list)假设your_list不是Noneget_first_list()并且get_second_list()必须始终返回可迭代)。
jfs

1
我认为您的意思是next(iter(your_list)),因为如果您向提供第二个参数iter,则表示第一个参数是可调用的。
罗伯特·罗斯尼

7
不,None这是的第二个参数next(),不是iter()next()如果给定,StopIteration则返回其第二个参数,而不是如果your_list为空则返回。
jfs

@JFSebastian建议的解决方案存在重复的问题:stackoverflow.com/a/18533669/144408
shahjapan

Answers:


205

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

1
最后一个选项几乎正是我要寻找的:很明显,它可以工作,并且不需要我定义新功能。如果不需要中断,我会说“完全”,因为省略中断的风险不大。但是这种方法具有真理的意义。
罗伯特·罗斯尼

哦,我一点都不喜欢。如果列表中的任何项目评估为False,则将该值丢弃并替换。如果""列表中有一个空字符串,则将其丢弃并替换为一个空列表[]。如果您有0,也请替换为[]。如果您False在那里,也要更换。等等,在特定情况下,您可能会避免这样做,但这是一个不好的习惯。
steveha

4
@steveha:bool(lst) 告诉我们是否len(lst) > 0不告诉我们lst包含哪些项目的任何信息,例如,bool([False]) == True;因此表达式[False] or []返回[False],对于return 来说也是[0] or [] 如此[0]
jfs

@RobertRossney:我添加是yield_first()为了避免break声明。
jfs 2012年

1
@ThomasAhle:都是真的。我已经更新了答案,以为非案件提供解决方案。
jfs

218

最好的方法是这样的:

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

您也可以在一行中完成此操作,但是对于程序员而言,阅读起来要困难得多:

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

哎呀 应该提到这是一个Python 2.4应用程序。
罗伯特·罗斯尼

15
@亚当:未来可能会使用next(iter(your_list), None)
jfs

@JFSebastian实际上next(iter(your_list))足够了
Brad Johnson

13
@BradleagheJohnson:错。它引发StopIteration而不是None按照OP的要求返回。试试:next(iter([]))
jfs 2015年

72
(get_list() or [None])[0]

那应该工作。

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

编辑:我在这里有一个稍微简单一点,但错误的版本。


4
这很聪明并且可行,但是我认为efotinis建议“如果没有其他则返回foo [0] foo”则更易于维护。
杰伊

3
问题是要寻求一线解决方案,所以我排除了这一点。
递归

如果get_list()返回None,则这些表达式均无效。您将收到“ TypeError:'NoneType'对象无法下标。” 或“ TypeError:+:'NoneType'和'list'的不受支持的操作数类型。”
罗伯特·罗斯尼,

4
在问题中,它说方法返回列表,没有一个不是列表。因此,鉴于这些要求,我仍然相信它们都可以工作。
递归

如果get_list()返回None,则现在应该可以正常工作,因为它不再被下标或添加。
罗伯特

32

python最惯用的方式是在list上使用next(),因为list是可迭代的。就像@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+


1
这样做的好处是,您不必将列表分配给变量(例如,return l[0] if l else None如果列表是libcomp)。当列表实际上是一个genexpr时,这会更好,因为您不必像这样构建整个列表next(iter(x for x in range(10) if x % 2 == 0), None)
RubenLaguna

如果您确定the_list不为空,请输入the_list[0]
kaya3

12

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

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

专业版:如果blah不可索引,则可以使用。专业版:语法不熟悉。虽然在ipython中进行黑客入侵和过滤时非常有用。


11

OP的解决方案即将到来,只有几件事可以使其变得更加Pythonic。

一方面,不需要获取列表的长度。如果检查,Python中的空列表的评估结果为False。只是简单地说

if 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]

最后,也许这是暗含的,但只是为了明确(因为explicit比隐式更好),您不应该让您的函数从另一个函数获取列表。只需将其作为参数传递即可。因此,最终结果将是

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


1
为什么“列出”是python中的保留字?或者,更重要的是,它在哪里说“列表”是python中的一个存储词?或者,更重要的是,您如何验证“列表”是python中的保留字?考虑到我可以声明一个名为“ list”的变量,因此它显然不是保留字。
Lasse V. Karlsen

2
点了。可以在这里找到Python中真正保留的单词tinyurl.com/424663。但是,最好将内置函数和类型视为保留的。list()实际上会生成一个列表。同去的字典,范围等
gotgenes

我将合并最后两行,消除临时变量(除非您进一步需要my_list)。这样可以提高可读性。
Svante

这几乎就是我所吸引的答案。
罗伯特·罗斯尼

1
get_first_item应该接受一个默认为None 的default参数(如dict.get)。因此,函数接口明确传达了如果my_list为空会发生的情况。
jfs

3
for item in get_list():
    return item

这是简洁而美丽的。
gotgenes

可悲的是,它不起作用。如果get_list()返回None,则抛出“ TypeError:'NoneType'对象不可迭代”异常。
罗伯特·罗斯尼

要解决的问题:“针对get_list()或[]中的项目:”
itsadok,2009年

5
该问题显然假定get_list返回一个列表。len和getitem调用其结果。
A. Coady

2

坦白地说,我认为没有更好的成语:您清楚而简洁-不需要任何“更好”的东西。也许吧,但是这的确是一个风格问题,你可以改变if len(list) > 0:使用if list:-一个空列表将永远评估为假。

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

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


在Python中,最好写if lst:而不是if len(lst) > 0:。另外,请勿将Python关键字用作变量名;它以眼泪结束。通常使用Llst作为某些列表的变量名。我确定在这种情况下,您并不是要暗示实际上建议将其list用作变量名,而只是表示“某些列表”。并且,“当lst [0]的值为假时”为+1。
steveha

是的,为了更清楚起见,我只是保持与OP相同的名称。但是您绝对是对的:应始终注意不要覆盖Python关键字。
罗布

2

返回第一项的Python习惯用法或无?

最Python化的方法是最受支持的答案所展示的内容,这是我阅读该问题时想到的第一件事。这是使用方法,首先将可能为空的列表传递给函数:

def get_first(l): 
    return l[0] if l else None

如果列表是从get_list函数返回的:

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

在此演示了其他方法,并附有说明

for

当我开始想办法做到这一点时,这是我想到的第二件事:

for item in get_list():
    return item

假定函数在此处结束,None如果get_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

2

关于惯用语,有一个itertools配方,称为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通常可扩展用于任何迭代,而不仅适用于列表。



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

这些是我得到的时间:

empty_lists:
  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秒
mixed_lists:
  index_first_item:
    0.791129秒
    0.743526秒
    0.744441秒
    平均0.759699秒
  return_first_item:
    0.784801秒
    0.785146秒
    0.840193秒
    平均0.803380秒

0
try:
    return a[0]
except IndexError:
    return None

1
异常通常不用于流控制。
马特·格林

我不认为延长代码长度并为其添加异常处理正是我所寻找的惯用法。
罗伯特·罗斯尼

3
但这是一个常见的Python习惯用法!出于性能原因,您可能不使用它。您将获得更好的性能,if len(a) > 0:但这被认为是好的样式。请注意这表示问题的方式:“尝试提取列表的标题,如果无法解决,请返回None”。Google搜索“ EAFP”或在这里查看:python.net/~goodger/projects/pycon/2007/idiomatic/…–
steveha

@steveha这取决于您的数据。如果len(a)通常> 0,而len(a)<1是极少数状态,则捕获异常实际上会更有效。
limscoder 2012年

@limscoder,我同意你的看法。我只是没有详细介绍何时出于性能原因您可能不使用它。我不是说你永远不会使用它。
steveha 2012年

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))

(尽可能避免重复)


0

这个怎么样:

(my_list and my_list[0]) or None

注意:这对于对象列表应该可以正常工作,但是如果根据以下注释列出数字或字符串,则可能会返回错误的答案。


怎么样([0,1] and 0) or None
肯利,2016年

这是一个好点...不安全地与当前形式的数字集合一起使用:(
VitalyB

与相同(['','A'] and '') or None
肯利,2016年

如果my_list[0]被评估为False,则结果应为None
肯利,2016年

2
my_list[0] if len(my_list) else None
尼古拉斯·汉密尔顿,

0

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

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

这只是一行(变成黑色),避免了依赖关系。


-1

使用and-or技巧:

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

-1

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

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

如果get_list()可以退货,None您可以使用:

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

优点:

-一条线

-您只需拨打get_list()一次

-容易理解


-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

-1

一些人建议做这样的事情:

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

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

我已经在自己的代码中多次创建了该错误!


3
这应该是有缺陷的答案下的评论,而不是独立的答案。
IJ肯尼迪



-3

不是C语言三元运算符的惯用python吗

cond and true_expr or false_expr

即。

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

如果a [0]是数字0或空字符串(或其他任何结果为false的字符串),则将返回None而不是a [0]的实际值。
亚当·罗森菲尔德
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.