具有列表理解的不同模式

时间:2013-10-29 13:57:59

标签: python list-comprehension

我面临着一个特殊的问题,最近我觉得有必要提供某种类型的列表理解。

例如:

[ re.search('xyz', l).group(0) for l in my_list if re.search('xyz', l) ]

现在,敏锐的读者可能已经观察到这个列表理解会进行两次正则表达式匹配。

我想消除这种'额外'的开销,不知何故失去了列表理解的优雅。以前有人遇到过这样的问题吗?如果是这样,他们是如何解决它的呢?

一般情况下,我有一个函数f(),我将其应用于列表中的值/对象x,现在我希望列表中包含f(x).b当且仅当f(x).a empty_list = [] for l in my_list: match = re.search('xyz', l) if match: empty_list.append(match.group(0)) 1}}满足某些条件。

我知道

empty_list = []
for x in my_list:
    val = f(x)
    if val.a == condition:
        empty_list.append(val.b)

或更一般地说:

{{1}}

是一种可能的解决方案,但这似乎过于冗长,我确信有更多的“pythonic”方法可以做到这一点。

4 个答案:

答案 0 :(得分:4)

使用生成器理解:

# Your original list of data.
my_list = []

# This 'knows' how to produce a new list of elements where each 
# element satisfies some test, here represented as `predicate`.
# Looks like a list comprehension, but note the surrounding parens.
passing = (x for x in my_list if predicate(x))

# This knows how to apply some function `f` to each element in the list.
mapped = (f(x) for x in passing)

# Now use the know-how above to actually create your list.
# Note that because you're using chained generators, you are 
# only iterating over the original list once!
results = list(mapped)

# Or if you don't need all the intermediate values at once...
for el in mapped:
  do_something_with(el)

答案 1 :(得分:3)

正如@tobias_k在评论中所说,你可以使用嵌套理解(这里是一个生成器):

>>> [m.group(0) 
     for m in (re.search('xyz', item) for item in ['abc', 'aaxyz', 'xyz'])
     if m is not None]
['xyz', 'xyz']

为了测试是否匹配,我总是使用if m is not None而不是if m。失败的匹配始终是None(并且对身份的测试非常快),而测试真实的匹配对象会调用匹配对象上的方法,这会对运行时产生足够大的影响。匹配对象。如果您没有100英寸的MB到GB范围的输入文本范围问题,这无关紧要,但它对于较小规模的问题没有任何影响。

答案 2 :(得分:1)

如果要避免计算两次,可以将正则表达式分解为中间生成器理解。

让我们从你的for-loop解决方案开始

for l in my_list:
  match = re.search('xyz', l)
  if match:
    empty_list.append(match.group(0))

我们可以通过人为地将匹配放在单元素列表中来将其转换为双循环:

for l in my_list:
  for match in [re.search('xyz', l)]:
    if match:
      empty_list.append(match.group(0))

由于现在只是for循环和if语句的堆栈,因此将它转换为嵌套列表理解是微不足道的:

[ match.group(0) for match in [re.search('xyz', l) for l in my_list] if match ]

事实上,如果你想避免生成不必要的列表,你可以将内部列表变成生成器理解

[ match.group(0) for match in (re.search('xyz', l) for l in my_list) if match ]

这最后一部分与您的问题无关,但我想要注意,某些语言允许您在列表解析中绑定名称,这样您就可以将整个事物编写为非嵌套列表理解。以下不是有效的Python代码,但可以

[ match.group(0) for l in my list, let match = re.search('xyz', l), if match ]

例如,在Haskell中你可以编写

[ group 0 match | elem <- myList, let match = reSearch "xyz" elem, match ]

这是有效的代码(模块化了我编写所有函数名称的事实)。

答案 3 :(得分:1)

就个人而言,我会这样做:

matches = [i.group(0) for i in filter(None, (re.search('xyz', i) for i in my_list))]

(如果你只是需要迭代它,那么没有list。)

filter生成生成器表达式的所有元素,其布尔值为True(在这种情况下,不是None)。

但是,如果您想使用此方法明确检查None,则需要执行以下操作:

matches = [i.group(0) for i in filter(lambda x: x is not None, (re.search('xyz', i) for i in my_list)))]