问题:Python成语返回第一项或无

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

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

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

I’m sure there’s a simpler way of doing this that’s just not occurring to me.

I’m calling a bunch of methods that return a list. The list may be empty. If the list is non-empty, I want to return the first item; otherwise, I want to return None. This code works:

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

It seems to me that there should be a simple one-line idiom for doing this, but for the life of me I can’t think of it. Is there?

Edit:

The reason that I’m looking for a one-line expression here is not that I like incredibly terse code, but because I’m having to write a lot of code like this:

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

What I’d like to be doing can certainly be accomplished with a function (and probably will be):

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

I posted the question because I’m frequently surprised by what simple expressions in Python can do, and I thought that writing a function was a silly thing to do if there was a simple expression could do the trick. But seeing these answers, it seems like a function is the simple solution.


回答 0

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

Python 2.6+

next(iter(your_list), None)

If your_list can be 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

Example:

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

Another option is to inline the above function:

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

To avoid break you could write:

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

Where:

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

回答 1

最好的方法是这样的:

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

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

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

The best way is this:

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

You could also do it in one line, but it’s much harder for the programmer to read:

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

回答 2

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

那应该工作。

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

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

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

That should work.

BTW I didn’t use the variable list, because that overwrites the builtin list() function.

Edit: I had a slightly simpler, but wrong version here earlier.


回答 3

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+

The most python idiomatic way is to use the next() on a iterator since list is iterable. just like what @J.F.Sebastian put in the comment on Dec 13, 2011.

next(iter(the_list), None) This returns None if the_list is empty. see next() Python 2.6+

or if you know for sure the_list is not empty:

iter(the_list).next() see iterator.next() Python 2.2+


回答 4

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

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

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

If you find yourself trying to pluck the first thing (or None) from a list comprehension you can switch to a generator to do it like:

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

Pro: works if blah isn’t indexable Con: it’s unfamiliar syntax. It’s useful while hacking around and filtering stuff in ipython though.


回答 5

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

The OP’s solution is nearly there, there are just a few things to make it more Pythonic.

For one, there’s no need to get the length of the list. Empty lists in Python evaluate to False in an if check. Just simply say

if list:

Additionally, it’s a very Bad Idea to assign to variables that overlap with reserved words. “list” is a reserved word in Python.

So let’s change that to

some_list = get_list()
if some_list:

A really important point that a lot of solutions here miss is that all Python functions/methods return None by default. Try the following below.

def does_nothing():
    pass

foo = does_nothing()
print foo

Unless you need to return None to terminate a function early, it’s unnecessary to explicitly return None. Quite succinctly, just return the first entry, should it exist.

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

And finally, perhaps this was implied, but just to be explicit (because explicit is better than implicit), you should not have your function get the list from another function; just pass it in as a parameter. So, the final result would be

def get_first_item(some_list): 
    if some_list:
        return list[0]

my_list = get_list()
first_item = get_first_item(my_list)

As I said, the OP was nearly there, and just a few touches give it the Python flavor you’re looking for.


回答 6

for item in get_list():
    return item
for item in get_list():
    return item

回答 7

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

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

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

Frankly speaking, I do not think there is a better idiom: your is clear and terse – no need for anything “better”. Maybe, but this is really a matter of taste, you could change if len(list) > 0: with if list: – an empty list will always evaluate to False.

On a related note, Python is not Perl (no pun intended!), you do not have to get the coolest code possible.
Actually, the worst code I have seen in Python, was also very cool :-) and completely unmaintainable.

By the way, most of the solution I have seen here do not take into consideration when list[0] evaluates to False (e.g. empty string, or zero) – in this case, they all return None and not the correct element.


回答 8

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

Python idiom to return first item or None?

The most Pythonic approach is what the most upvoted answer demonstrated, and it was the first thing to come to my mind when I read the question. Here’s how to use it, first if the possibly empty list is passed into a function:

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

And if the list is returned from a get_list function:

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

Other ways demonstrated to do this here, with explanations

for

When I began trying to think of clever ways to do this, this is the second thing I thought of:

for item in get_list():
    return item

This presumes the function ends here, implicitly returning None if get_list returns an empty list. The below explicit code is exactly equivalent:

for item in get_list():
    return item
return None

if some_list

The following was also proposed (I corrected the incorrect variable name) which also uses the implicit None. This would be preferable to the above, as it uses the logical check instead of an iteration that may not happen. This should be easier to understand immediately what is happening. But if we’re writing for readability and maintainability, we should also add the explicit return None at the end:

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

slice or [None] and select zeroth index

This one is also in the most up-voted answer:

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

The slice is unnecessary, and creates an extra one-item list in memory. The following should be more performant. To explain, or returns the second element if the first is False in a boolean context, so if get_list returns an empty list, the expression contained in the parentheses will return a list with ‘None’, which will then be accessed by the 0 index:

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

The next one uses the fact that and returns the second item if the first is True in a boolean context, and since it references my_list twice, it is no better than the ternary expression (and technically not a one-liner):

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

next

Then we have the following clever use of the builtin next and iter

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

To explain, iter returns an iterator with a .next method. (.__next__ in Python 3.) Then the builtin next calls that .next method, and if the iterator is exhausted, returns the default we give, None.

redundant ternary expression (a if b else c) and circling back

The below was proposed, but the inverse would be preferable, as logic is usually better understood in the positive instead of the negative. Since get_list is called twice, unless the result is memoized in some way, this would perform poorly:

return None if not get_list() else get_list()[0]

The better inverse:

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

Even better, use a local variable so that get_list is only called one time, and you have the recommended Pythonic solution first discussed:

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

回答 9

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

Regarding idioms, there is an itertools recipe called nth.

From itertools recipes:

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

If you want one-liners, consider installing a library that implements this recipe for you, e.g. more_itertools:

import more_itertools as mit

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

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

Another tool is available that only returns the first item, called more_itertools.first.

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

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

These itertools scale generically for any iterable, not only for lists.


回答 10

my_list[0] if len(my_list) else None

my_list[0] if len(my_list) else None


回答 11

出于好奇,我选择了两个解决方案。使用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秒

Out of curiosity, I ran timings on two of the solutions. The solution which uses a return statement to prematurely end a for loop is slightly more costly on my machine with Python 2.5.1, I suspect this has to do with setting up the iterable.

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

These are the timings I got:

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

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

回答 13

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

(尽可能避免重复)

def head(iterable):
    try:
        return iter(iterable).next()
    except StopIteration:
        return None

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

BTW: I’d rework your general program flow into something like this:

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

(Avoiding repetition whenever possible)


回答 14

这个怎么样:

(my_list and my_list[0]) or None

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

How about this:

(my_list and my_list[0]) or None

Note: This should work fine for lists of objects but it might return incorrect answer in case of number or string list per the comments below.


回答 15

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

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

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

Not sure how pythonic this is but until there is a first function in the library I include this in the source:

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

It’s just one line (conforms to black) and avoids dependencies.


回答 16

使用and-or技巧:

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

Using the and-or trick:

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

回答 17

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

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

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

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

优点:

-一条线

-您只需拨打get_list()一次

-容易理解

Probably not the fastest solution, but nobody mentioned this option:

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

if get_list() can return None you can use:

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

Advantages:

-one line

-you just call get_list() once

-easy to understand


回答 18

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

我个人找到了尝试,除了风格更清晰的阅读

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

My use case was only to set the value of a local variable.

Personally I found the try and except style cleaner to read

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

than slicing a list.

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

回答 19

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

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

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

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

Several people have suggested doing something like this:

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

That works in many cases, but it will only work if list[0] is not equal to 0, False, or an empty string. If list[0] is 0, False, or an empty string, the method will incorrectly return None.

I’ve created this bug in my own code one too many times !


回答 20

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

我不会尝试对其进行更多压缩,一个衬板似乎比详细版本更难读。而且,如果您使用提取方法,那是一个衬里;)

You could use Extract Method. In other words extract that code into a method which you’d then call.

I wouldn’t try to compress it much more, the one liners seem harder to read than the verbose version. And if you use Extract Method, it’s a one liner ;)


回答 21

if mylist != []:

       print(mylist[0])

   else:

       print(None)
if mylist != []:

       print(mylist[0])

   else:

       print(None)

回答 22

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

cond and true_expr or false_expr

即。

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

isn’t the idiomatic python equivalent to C-style ternary operators

cond and true_expr or false_expr

ie.

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

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。