-1

什么 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:我想知道,为什么大多数人倾向于不回答问题而是批评提出的例子?问题实际上是关于最喜欢的列表理解结构。

4

4 回答 4

13

any并且all是 2.5 标准 Python 的一部分。无需制作您自己的这些版本。如果可能的话,还有官方版本anyall短路评估,以提高性能。您的版本总是遍历整个列表。

如果您想要一个接受谓词的版本,请使用这样的东西来利用现有的anyall功能:

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 函数是一种不好的做法。

于 2009-11-23T15:48:23.280 回答
5

在很多情况下,列表推导(简称 LC)比等效的生成器表达式(简称 GE,即使用圆括号而不是方括号,一次生成一个项目而不是比“一开始就全部批量”)。

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

从本质上讲,与 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)...的语法快捷方式!

于 2009-11-23T16:38:34.553 回答
4

这个解决方案会影响内置函数,这通常是一个主意。然而,使用感觉相当 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)

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

于 2009-11-23T16:06:35.150 回答
2

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

于 2009-11-23T16:44:59.033 回答