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

时间:2008-12-12 19:56:03

标签: python idioms python-2.4

我确信有一种更简单的方法可以解决这个问题。

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

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

25 个答案:

答案 0 :(得分:191)

最好的方法是:

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

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

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

答案 1 :(得分:146)

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

答案 2 :(得分:63)

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

这应该有效。

BTW我没有使用变量list,因为它会覆盖内置list()函数。

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

答案 3 :(得分:31)

最python习惯的方法是在迭代器上使用next(),因为列表是 iterable 。就像@ J.F.Sebastian在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+

答案 4 :(得分:10)

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]

最后,也许这是隐含的,但只是为了明确(因为explicit is better than implicit),你不应该让你的函数从另一个函数中获取列表;只是将其作为参数传递。所以,最终结果将是

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

答案 5 :(得分:10)

如果您发现自己试图从列表理解中挑选第一个(或无),您可以切换到生成器,如下所示:

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

Pro:如果blah不可索引,则有效Con:它是不熟悉的语法。虽然在ipython中进行黑客攻击并过滤内容非常有用。

答案 6 :(得分:3)

for item in get_list():
    return item

答案 7 :(得分:2)

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

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

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

答案 8 :(得分:2)

关于习语,有itertools recipe名为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一般适用于任何可迭代的,不仅适用于列表。

答案 9 :(得分:2)

  

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返回一个空列表,则括号中包含的表达式将返回一个带有'的列表'无',然后由0索引访问:

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

下一个使用的事实是,如果第一个项是布尔上下文中的True,则返回第二个项,并且因为它引用了my_list两次,所以它并不比三元表达式好(并且技术上不是一个-liner):

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

next

然后我们巧妙地使用了内置nextiter

return next(iter(get_list()), None)

为了解释,iter返回带有.next方法的迭代器。 (3中的.__next__。)然后内置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

答案 10 :(得分:1)

my_list[0] if len(my_list) else None

答案 11 :(得分:1)

出于好奇,我对两个解决方案进行了计时。使用返回语句过早结束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.

答案 12 :(得分: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))

(尽可能避免重复)

答案 13 :(得分: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)

答案 14 :(得分:0)

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

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

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

答案 15 :(得分:0)

这个怎么样:

(my_list and my_list[0]) or None

注意:这应该适用于对象列表,但如果数字或字符串列表符合以下注释,则可能会返回错误答案。

答案 16 :(得分:0)

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

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

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

  
    

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

  

答案 17 :(得分:0)

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

答案 18 :(得分:-1)

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

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

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

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

优点:

-one line

- 您只需拨打get_list()一次

- 易于理解

答案 19 :(得分:-1)

那么:next(iter(get_list()), None)? 可能不是这里最快的,但它是标准的(从Python 2.6开始)并且简洁。

答案 20 :(得分:-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

答案 21 :(得分:-1)

if mylist != []:

       print(mylist[0])

   else:

       print(None)

答案 22 :(得分:-1)

使用and-or trick:

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

答案 23 :(得分:-1)

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

我不会尝试更多地压缩它,一个衬垫似乎比冗长的版本更难阅读。如果你使用Extract Method,它就是一个衬里;)

答案 24 :(得分:-3)

不是等价于C风格的三元运算符的惯用python

cond and true_expr or false_expr

即。

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