Python成语返回第一项或None
我确信有一个简单的方法来做到这一点,只是没有发生在我身上。
我打电话了一堆返回列表的方法。 该列表可能是空的。 如果列表非空,我想返回第一个项目; 否则,我想返回None。 此代码工作:
my_list = get_list() if len(my_list) > 0: return my_list[0] return None 在我看来,这样做应该有一个简单的一行成语,但是对于我来说,我想不起来。 在那儿?
编辑:
我在这里寻找单行expression式的原因并不是我喜欢难以置信的简洁代码,而是因为我不得不写很多这样的代码:
 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中的简单expression式所惊讶,我认为编写一个函数是一件很愚蠢的事,如果有一个简单的expression式可以做到这一点。 但看到这些答案,似乎是一个function是简单的解决scheme。
Python 2.6+
 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: ... 
另一种select是内联上述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 
 为了避免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 
最好的办法是这样的:
 a = get_list() return a[0] if a else None 
你也可以用一行来完成,但程序员读起来要困难得多:
 return (get_list()[:1] or [None])[0] 
 (get_list() or [None])[0] 
这应该工作。
 顺便说一句,我没有使用variableslist ,因为这会覆盖内置的list()函数。 
编辑:我有一个稍微简单,但在这里错误的版本。
最常用的Python方法是在迭代器上使用next(),因为列表是可迭代的 。 就像@JFSebastian在2011年12月13日发表的评论中所说的那样。
  next(iter(the_list), None)如果the_list为空,则返回None。 看下一个()Python 2.6+ 
 或者如果你确定the_list不是空的: 
  iter(the_list).next()请参阅iterator.next()Python 2.2+ 
OP的解决scheme几乎就在那里,只有几件事情使它变得更加Pythonic。
首先,没有必要得到列表的长度。 Python中的空列表在if检查中评估为False。 只是简单地说
 if list: 
此外,分配与保留字重叠的variables是一个非常糟糕的想法。 “list”是Python中的保留字。
所以让我们改变
 some_list = get_list() if some_list: 
这里有很多解决scheme非常重要, 所有的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风味。
如果你发现自己试图从列表理解中摘下第一个东西(或无),你可以切换到一个生成器来执行它,如:
 next((x for x in blah if cond), None) 
Pro:如果blah不可索引,则工作。Con:这是不熟悉的语法。 虽然在ipython中进行黑客入侵和过滤是非常有用的。
 for item in get_list(): return item 
对于这个问题,这里还有另一种可能性。
 return None if not get_list() else get_list()[0] 
好处:该方法处理get_list为None的情况,不使用try / except或assignment。 据我所知,上面的实现都不能处理这种可能性
垮台:get_list()被调用两次,相当不必要的,特别是如果列表很长和/或调用该函数时创build。
事实是,在我看来,更“Python”是提供可读的代码,而不仅仅是因为你可以做出一行代码:)我不得不承认,我多次因为不必要地压缩Python代码而感到内疚,我很感动,我可以做一个复杂的function,看起来很小:)
编辑:用户“hasen j”在下面评论,上面的条件expression式在Python 2.5中是新的,如下所述: https : //docs.python.org/whatsnew/2.5.html#pep-308 。 谢谢,哈森!
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 
其他方式表明要做到这一点,解释
 for 
当我开始想办法做到这一点时,我想到的第二件事就是:
 for item in get_list(): return item 
 这假定函数在这里结束,如果get_list返回一个空列表,隐式返回None 。 下面的显式代码是完全等价的: 
 for item in get_list(): return item return None 
 if some_list 
 还提出了以下(我更正了不正确的variables名称),它也使用隐式None 。 这将比上面更好,因为它使用逻辑检查而不是可能不会发生的迭代。 这应该更容易理解发生的事情。 但是,如果我们为了可读性和可维护性而编写代码,我们还应该在末尾添加明确的return None : 
 some_list = get_list() if some_list: return some_list[0] 
 切片or [None]并select第零索引 
这也是最受赞赏的答案:
 return (get_list()[:1] or [None])[0] 
 切片是不必要的,并在内存中创build一个额外的单项清单。 以下应该是更高性能。 要解释, or返回第二个元素,如果第一个元素是布尔上下文中的False ,那么如果get_list返回一个空列表,那么包含在括号中的expression式将返回一个带有“None”的列表,然后将被0索引: 
 return (get_list() or [None])[0] 
 下一个使用的事实是,并返回第二个项目,如果第一个是在布尔上下文中为True ,并且因为它引用my_list两次,它不比三元expression式(技术上不是一个单行): 
 my_list = get_list() return (my_list and my_list[0]) or None 
 next 
接下来我们有如下巧妙的内build使用方法
 return next(iter(get_list()), None) 
 为了解释, iter使用.next方法返回一个迭代器。  (Python 3中的.__next__ )然后内置next调用.next方法,如果迭代器耗尽,则返回默认值None 。 
 多余的三元expression式( a if b else c )并回转 
 下面是提出的,但反过来是可取的,因为逻辑通常更好地理解在积极的而不是消极的。 由于get_list被调用两次,除非结果以某种方式被记忆,否则这将performance不佳: 
 return None if not get_list() else get_list()[0] 
更好的反面:
 return get_list()[0] if get_list() else None 
 更好的get_list是使用一个局部variables,这样get_list只被调用一次,而且你首先讨论了推荐的Pythonic解决scheme: 
 l = get_list() return l[0] if l else None 
 坦率地说,我认为没有更好的习惯用语:你的语言清晰简洁 – 不需要任何“更好”的东西。 也许,但这真的是一个品味的问题,你可以改变, if len(list) > 0: if list: – 一个空列表将始终评价为False。 
 在相关说明中,Python 不是 Perl(不是双关语!),您不必获得最酷的代码。 
 实际上,我在Python中看到的最糟糕的代码也非常酷:-),完全无法维护。 
顺便说一下,我在这里看到的大部分解决scheme都没有考虑到list [0]的计算结果为False(例如空string或零) – 在这种情况下,它们都返回None,而不是正确的元素。
出于好奇,我跑了两个解决scheme的时间。 使用return语句提前结束for循环的解决scheme,在我的机器上使用Python 2.5.1的成本稍高,我怀疑这与设置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 
这是我得到的时机:
 empty_lists:
   index_first_item:
     0.748353秒
     0.741086秒
     0.741191秒
    平均0.743543秒
   return_first_item:
     0.785511秒
     0.822178秒
     0.782846秒
    平均0.796845秒
 full_lists:
   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秒。
这个怎么样:
 (my_list and my_list[0]) or None 
注意:对于对象列表,这应该可以正常工作,但是如果数字或string列表根据下面的注释,它可能会返回错误的答案。
 try: return a[0] except IndexError: return None 
 def head(iterable): try: return iter(iterable).next() except StopIteration: return None print head(xrange(42, 1000) # 42 print head([]) # None 
顺便说一句:我会重新将您的一般程序stream程如下所示:
 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)) 
(尽可能避免重复)
 关于成语,有一个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通常可以为任何迭代进行缩放,而不仅仅是用于列表。
你可以使用提取方法 。 换句话说,将这些代码提取到一个你将要调用的方法中。
我不会试图压缩它,这一行似乎比详细的版本更难读。 如果你使用提取方法,这是一个class轮;)
使用和 – 或技巧:
 a = get_list() return a and a[0] or None 
 那么怎么样: next(iter(get_list()), None) ? 可能不是这里最快的,但标准(从Python 2.6开始)和简洁。 
可能不是最快的解决scheme,但没有人提到这个选项:
 dict(enumerate(get_list())).get(0) 
 如果get_list()可以返回None您可以使用: 
 dict(enumerate(get_list() or [])).get(0) 
优点:
-一条线
  – 你只需调用get_list()一次 
-容易明白
我的用例只是设置一个局部variables的值。
我个人发现尝试和除了风格清洁剂阅读
 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 
 if mylist != []: print(mylist[0]) else: print(None) 
有几个人build议做这样的事情:
 list = get_list() return list and list[0] or None 
这在很多情况下都是有效的,但是只有当列表[0]不等于0,False或者一个空string时,它才会起作用。 如果list [0]是0,False或空string,则该方法将错误地返回None。
我在自己的代码中创build了这个bug太多了!
不是与C式三元运算符相当的惯用python
 cond and true_expr or false_expr 
即。
 list = get_list() return list and list[0] or None