我相信还有一种更简单的方法,只是我没有想到。
我调用了一系列返回列表的方法。列表可能是空的。如果列表非空,我想返回第一项;否则,我想返回None。这段代码:
1 2 3 | my_list = get_list() if len(my_list) > 0: return my_list[0] return None |
在我看来,应该有一个简单的单行成语来做这件事,但是我怎么也想不出来。是吗?
编辑:
我在这里寻找一个单行表达式的原因不是因为我喜欢非常简洁的代码,而是因为我必须写很多这样的代码:
1 2 3 4 5 6 7 8 | 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 |
我想做的事情当然可以用一个函数来完成(很可能会):
1 2 3 4 5 6 7 8 9 | 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中的简单表达式所能做的事情感到惊讶,我认为如果有一个简单的表达式可以做到这一点,那么编写一个函数就是一件愚蠢的事情。但是看到这些答案,函数似乎是简单的解。
最好的方法是:
1 2 | a = get_list() return a[0] if a else None |
你也可以用一行来写,但是程序员很难读懂:
1 | return (get_list()[:1] or [None])[0] |
Python 2.6 +
1 | next(iter(your_list), None) |
如果
1 | next(iter(your_list or []), None) |
Python 2.4
1 2 3 4 5 | def get_first(iterable, default=None): if iterable: for item in iterable: return item return default |
例子:
1 2 3 4 5 6 | x = get_first(get_first_list()) if x: ... y = get_first(get_second_list()) if y: ... |
另一个选项是内联上述函数:
1 2 3 4 5 6 | 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 |
为了避免
1 2 3 4 | for x in yield_first(get_first_list()): x # process x for y in yield_first(get_second_list()): y # process y |
地点:
1 2 3 4 | def yield_first(iterable): for item in iterable or []: yield item return |
1 | (get_list() or [None])[0] |
这应该工作。
顺便说一下,我没有使用变量
编辑:我之前有一个稍微简单一点的版本,但是在这里是错误的。
python最惯用的方法是在迭代器上使用next(),因为list是可迭代的。就像@J.F。塞巴斯蒂安在2011年12月13日发表了评论。
如果
或者如果你确定
OP的解决方案已经差不多了,这里有一些东西可以让它更符合python的风格。
首先,不需要知道列表的长度。Python中的空列表在if检查中计算为False。只是简单地说
1 | if list: |
此外,给与保留字重叠的变量赋值是一个非常糟糕的主意。"list"是Python中的一个保留字。
我们把它变成
1 2 | some_list = get_list() if some_list: |
这里很多解决方案都忽略了一个非常重要的问题,即所有Python函数/方法默认情况下都不返回任何值。试试下面的方法。
1 2 3 4 5 | def does_nothing(): pass foo = does_nothing() print foo |
除非需要返回None来提前终止函数,否则没有必要显式返回None。非常简洁,只要返回第一个条目,如果它存在的话。
1 2 3 | some_list = get_list() if some_list: return list[0] |
最后,也许这是隐含的,但只是显式的(因为显式比隐式更好),你不应该让你的函数从另一个函数得到列表;把它作为参数传递进来。最后的结果是
1 2 3 4 5 6 | 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 | next((x for x in blah if cond), None) |
优点:如果废话不能索引,那就可以用。缺点:语法不熟悉。不过,在ipython中进行黑客攻击和过滤时,它非常有用。
1 2 | for item in get_list(): return item |
Python idiom to return first item or None?
最具python风格的方法就是最受欢迎的答案,当我读到这个问题时,我首先想到的就是这个答案。下面是如何使用它,首先,如果可能的空列表被传递到一个函数:
1 2 | def get_first(l): return l[0] if l else None |
如果列表是从
1 2 | l = get_list() return l[0] if l else None |
这里演示了其他实现此目的的方法,并给出了解释
for
当我开始想一些聪明的方法来做这件事的时候,这是我想到的第二件事:
1 2 | for item in get_list(): return item |
这假设函数在这里结束,如果
1 2 3 | for item in get_list(): return item return None |
if some_list
下面还提出了(我纠正了不正确的变量名),它也使用了隐式的
1 2 3 | some_list = get_list() if some_list: return some_list[0] |
切片
这个问题的答案也是最受欢迎的:
1 | return (get_list()[:1] or [None])[0] |
切片是不必要的,并在内存中创建一个额外的单条目列表。下面应该更有性能。为了解释,
1 | return (get_list() or [None])[0] |
下一个使用的事实是,如果第一个是布尔上下文中的
1 2 | my_list = get_list() return (my_list and my_list[0]) or None |
next
然后我们就可以巧妙地使用内置函数
1 | return next(iter(get_list()), None) |
为了解释,
下面的建议,但最好是相反的,因为逻辑通常更好地理解为积极的而不是消极的。由于
1 | return None if not get_list() else get_list()[0] |
更好的逆:
1 | return get_list()[0] if get_list() else None |
更好的是,使用一个局部变量,这样
1 2 | l = get_list() return l[0] if l else None |
坦率地说,我认为没有比这更好的成语了:your is clear and short - no need for anything"better"。也许,但这确实是一个品味问题,您可以使用
另外,Python不是Perl(没有双关语的意思!),您不必获得尽可能最酷的代码。实际上,我在Python中见过的最糟糕的代码也是非常酷的:-),而且完全不可维护。
顺便说一下,我在这里看到的大多数解决方案都没有考虑list[0]计算为False时的情况(例如空字符串或零)——在本例中,它们都返回None,并且没有返回正确的元素。
关于习惯用法,有一个itertools配方称为
出现从itertools食谱:
1 2 3 | def nth(iterable, n, default=None): "Returns the nth item or a default value" return next(islice(iterable, n, None), default) |
如果您需要一行程序,可以考虑安装一个库来实现这个配方,例如
1 2 3 4 5 6 7 | import more_itertools as mit mit.nth([3, 2, 1], 0) # 3 mit.nth([], 0) # default is `None` # None |
另一个工具是可用的,它只返回第一项,称为
1 2 3 4 5 | mit.first([3, 2, 1]) # 3 mit.first([], default=None) # None |
这些迭代工具不仅适用于列表,而且适用于任何可迭代的。
出于好奇,我对其中两个解决方案进行了计时。在我的机器上,使用return语句提前结束for循环的解决方案在Python 2.5.1中稍微昂贵一些,我怀疑这与设置iterable有关。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | 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.
1 2 3 4 5 6 7 8 | def head(iterable): try: return iter(iterable).next() except StopIteration: return None print head(xrange(42, 1000) # 42 print head([]) # None |
顺便说一句:我要把你的程序流程修改成这样:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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)) |
(尽可能避免重复)
这个怎么样:
注意:对于对象列表,这应该可以工作得很好,但是对于下面评论中的数字或字符串列表,它可能返回不正确的答案。
一些人建议这样做:
1 2 | list = get_list() return list and list[0] or None |
这在很多情况下都是有效的,但是只有当list[0]不等于0、False或空字符串时才有效。如果list[0]为0、False或空字符串,该方法将错误地返回None。
I've created this bug in my own code one too many times !
那么:
使用and-or技巧:
1 2 | a = get_list() return a and a[0] or None |
可能不是最快的解决方案,但是没有人提到这个选项:
1 | dict(enumerate(get_list())).get(0) |
如果
1 | dict(enumerate(get_list() or [])).get(0) |
优点:
一线
-你只要打一次电话给
容易理解
我的用例只是设置一个局部变量的值。
就我个人而言,我发现尝试和除风格更干净的阅读
1 2 3 4 | items = [10, 20] try: first_item = items[0] except IndexError: first_item = None print first_item |
而不是切一张清单。
1 2 3 | items = [10, 20] first_item = (items[:1] or [None, ])[0] print first_item |
1 2 3 4 | try: return a[0] except IndexError: return None |
1 2 3 4 5 6 7 | if mylist != []: print(mylist[0]) else: print(None) |
您可以使用Extract方法。换句话说,将代码提取到一个方法中,然后调用该方法。
我不会试图压缩它太多,一行代码似乎比冗长的版本更难读。如果你使用提取方法,它是一行;)
难道惯用的python不等同于c风格的三元运算符吗
1 | cond and true_expr or false_expr |
ie。
1 2 | list = get_list() return list and list[0] or None |