【问题标题】:Python: list comprehensions vs. lambdaPython:列表推导与 lambda
【发布时间】:2013-03-13 11:30:13
【问题描述】:

我想从包含“b”作为第一个元素的子列表中检索整数值(b 只会在列表中出现一次)

我想到了这两种方法:

foo = [["a", 5], ["b", 10], ["c", 100]]

y = filter(lambda x: x[0] == "b", foo)
print y[0][1]

z = [foo[i][1] for i in range(len(foo)) if foo[i][0] == "b"] 
print z[0]

它们都有效。这两种方法中的任何一种更可取(关于运行时间),还有更好的第三种方法吗?

【问题讨论】:

  • z = [x[1] for x in foo if x[0] == 'b'] 甚至更干净一些。
  • [b for a, b in foo if a == 'b']

标签: python list lambda list-comprehension


【解决方案1】:

当列表如此之小时,两者之间没有显着差异。如果输入列表可以变大,那么就会出现更严重的问题:您正在遍历整个列表,而您可能会在第一个元素处停止。您可以使用 for 循环来完成此操作,但如果您想使用类似理解的语句,请使用 生成器表达式

# like list comprehensions but with () instead of []
gen = (b for a, b in foo if a == 'b')
my_element = next(gen)

或者简单地说:

my_element = next(b for a, b in foo if a == 'b')

如果您想了解有关生成器表达式的更多信息,请查看PEP 289


请注意,即使使用生成器和迭代器,您也有不止一种选择。

# Python 3:
my_element = next(filter(lambda x: x[0] == 'b', foo))

# Python 2:
from itertools import ifilter
my_element = next(ifilter(lambda (x, y): x == 'b', foo))

我个人不喜欢也不推荐这个,因为它的可读性要差得多。事实证明,这实际上比我的第一个 sn-p 慢,但更一般地说,使用 filter() 而不是生成器表达式在某些特殊情况下可能会更快。

无论如何,如果您需要对代码进行基准测试,我建议您使用the timeit module

【讨论】:

  • next(v for k,v in foo if k == "b") -- 使用next() 而不是.next() -- 更加向前兼容。
  • 在我的时间里,ifilter 版本如果必须检查两个以上的项目就会失败。
  • 出于兴趣:您在哪里可以读到用逗号分隔两个变量是在 for 循环中访问列表元素的一种方法?这对我来说是新的,我发现它非常有用。我到现在才知道my_element = next(x[1] for x in foo if x[0] == 'b')
  • @HelmHammerhand 它被称为解包,并在此处记录:docs.python.org/2/reference/… 但您可以在 for 循环(如上)和函数参数列表(如上面的 lambda)中使用类似的形式
【解决方案2】:

这比 DavidE 的回答慢(我计时了),但优点是简单:

z = dict(foo)['b']

当然,它假定您的键都是可散列的,但如果它们是字符串,那就没问题了。 如果您需要进行多次查找,尽管这绝对是要走的路(只要确保只转换为 dict 一次)。

【讨论】:

  • +1 为简单起见,但我认为这不会加快速度,因为它需要对序列进行完整迭代。如果速度是问题,我会进行基准测试(和配置文件),因为它取决于很多事情(列表大小、内存要求、哈希冲突),具体取决于输入。
  • @DavideR。我对它进行了计时,对于最多大约 10,000 个元素的列表,如果它是最后一个元素,则您的解决方案的速度大致相同,或者平均速度是其两倍。对于 100,000 个元素的列表,您平均可以快 4 倍。
猜你喜欢
  • 1970-01-01
  • 2016-02-04
  • 1970-01-01
  • 2016-02-13
  • 2010-11-07
  • 2012-05-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多