【问题标题】:Most useful list-comprehension construction?最有用的列表理解结构?
【发布时间】:2009-11-23 15:44:23
【问题描述】:

什么 Python 的用户自定义列表理解结构最有用?

我创建了以下两个量词,用于执行不同的验证操作:

def every(f, L): return not (False in [f(x) for x in L])
def some(f, L): return True in [f(x) for x in L]

下面提出了一个优化版本(需要 Python 2.5+):

def every(f, L): return all(f(x) for x in L)
def some(f, L): return any(f(x) for x in L) 

那么,它是如何工作的?

"""For all x in [1,4,9] there exists such y from [1,2,3] that x = y**2"""
answer = every([1,4,9], lambda x: some([1,2,3], lambda y: y**2 == x))

使用此类操作,您可以轻松进行智能验证,例如:

"""There exists at least one bot in a room which has a life below 30%"""
answer = some(bots_in_this_room, lambda x: x.life < 0.3)

等等,您甚至可以使用此类量词回答非常复杂的问题。当然,Python 中没有无限列表(嘿,它不是 Haskell :)),但是 Python 的列表推导非常实用。

你有自己喜欢的列表理解结构吗?

PS:我想知道,为什么大多数人倾向于不回答问题而是批评所提供的示例?问题实际上是关于最喜欢的列表理解构造。

【问题讨论】:

  • 社区维基,如果有的话
  • 这些函数是从 2.5 开始内置的。 docs.python.org/library/functions.html
  • 我意识到它们并不等同。显然,内置函数不允许您传入函数。
  • 使用生成器的 Python 中有类似“无限列表”的对象。 itertools.count() 例如返回 0,1,2,3,...

标签: python list list-comprehension


【解决方案1】:

any和all是2.5标准Python的一部分。无需制作您自己的这些版本。此外,如果可能,any 和all 的官方版本会缩短评估,从而提高性能。您的版本始终遍历整个列表。

如果您想要一个接受谓词的版本,请使用这样的东西,利用现有的 any 和 all 函数:

def anyWithPredicate(predicate, l): return any(predicate(x) for x in l) 
def allWithPredicate(predicate, l): return all(predicate(x) for x in l) 

不过,我并不特别认为需要这些功能,因为它并不能真正节省很多打字时间。

此外,使用您自己的同名但行为不同的函数隐藏现有的标准 Python 函数是一种不好的做法。

【讨论】:

  • 完全错误。可能我选择了令人困惑的名字。在 Python 中, all 和 any 做完全不同的事情!如果 bool(x) 对于 iterable 中的所有值 x 为 True,则 Python 的 buildin all 返回 True。我的函数有 2 个参数 - 一个函数和一个列表。
  • 谓词是闭包的 .net 术语。这是我记得第一次看到它在 python 中使用。实际上,anyWithPredicate 是一个非标准的 Python 命名约定
  • 来自docs.python.org/library/itertools.html "itertools.dropwhile(predicate, iterable)"
  • @Bryan McLemore:“谓词”不是 .NET 术语(谓词在 Anders Hjelsberg 还是个孩子的时候在 LISP 中使用过),也不是 .NET 用于闭包的术语。
  • 你在开玩笑吗? “谓词”的当前含义自 19 世纪以来一直在使用。
【解决方案2】:

列表推导式(简称 LC)比等价的生成器表达式(简称 GE,即使用圆括号代替方括号,在一次,而不是“一开始就全部批量”)。

有时,您可以通过“投资”额外的内存来一次保存所有列表来获得一点额外的速度,这取决于一个或另一个版本的 Python 上的优化和垃圾收集的变幻莫测,但这几乎不会带来实质性的额外用处LC vs GE。

本质上,与 GE 相比,要从 LC 中获得大量额外的使用,您需要在序列上本质上需要“不止一次通过”的用例。在这种情况下,GE 将要求您每次生成序列一次,而使用 LC,您可以生成一次序列,然后对其执行多次传递(只需支付一次生成成本)。如果 GE / LC 基于一个不能轻易重启的底层迭代器(例如,一个实际上是 Unix 管道的“文件”),多代也可能会出现问题。

例如,假设您正在阅读一个非空的打开文本文件f,其中包含一堆(文本表示)由空格分隔的数字(包括这里和那里的换行符、空行等)。您可以使用 GE 将其转换为数字序列:

G = (float(s) for line in f for s in line.split())

或信用证:

L = [float(s) for line in f for s in line.split()]

哪个更好?取决于你用它做什么(即用例!)。如果你想要的只是总和,sum(G) 和 sum(L) 也可以。如果你想要平均值, sum(L)/len(L) 对列表很好,但对生成器不起作用——考虑到“重新启动 f”的困难,为了避免中间列表,你必须做类似的事情:

tot = 0.0
for i, x in enumerate(G): tot += x
return tot/(i+1)

没有任何地方像return sum(L)/len(L) 那样活泼、快速、简洁和优雅。

请记住,sorted(G) 确实返回了一个列表(不可避免地),因此在这种情况下,L.sort()(就地)是大致等价的—— sorted(L) 将是多余的(因为现在你有 两个列表)。因此,当需要排序时,由于简洁,通常可能首选生成器。

总而言之,由于 L 等同于 list(G),因此很难对通过标点符号(方括号而不是圆括号)而不是单个、简短、可发音且明显的单词来表达它的能力感到非常兴奋像list;-)。这就是 LC 的全部内容——list(some_genexp)...的基于标点符号的语法快捷方式...!

【讨论】:

    【解决方案3】:

    这个解决方案隐藏了内置函数,这通常是一个坏的想法。然而,使用感觉相当 Pythonic,并且它保留了原始功能。

    请注意,有几种方法可以根据测试进行优化,包括将导入移到模块级别并将 f 的默认值更改为 None 并对其进行测试,而不是像我一样使用默认 lambda。

    def any(l, f=lambda x: x):
        from __builtin__ import any as _any
        return _any(f(x) for x in l)
    
    def all(l, f=lambda x: x):
        from __builtin__ import all as _all
        return _all(f(x) for x in l)
    

    只是把它放在那里以供考虑,看看人们对做一些可能很肮脏的事情有何看法。

    【讨论】:

      【解决方案4】:

      供您参考,python 3.x 中模块 itertools 的 documentation 列出了一些非常不错的生成器函数。

      【讨论】:

        猜你喜欢
        • 2013-09-05
        • 1970-01-01
        • 1970-01-01
        • 2021-10-10
        • 2017-01-10
        • 2018-02-21
        • 1970-01-01
        • 2015-03-01
        • 1970-01-01
        相关资源
        最近更新 更多