【问题标题】:How to use re match objects in a list comprehension如何在列表理解中使用重新匹配对象
【发布时间】:2011-01-27 01:18:24
【问题描述】:

我有一个函数可以从字符串列表中挑选出块并将它们作为另一个列表返回:

def filterPick(lines,regex):
    result = []
    for l in lines:
        match = re.search(regex,l)
        if match:
            result += [match.group(1)]
    return result

有没有办法将其重新表述为列表理解?显然它是相当清楚的;只是好奇。


感谢那些做出贡献的人,特别提到@Alex。这是我最终得到的浓缩版;正则表达式匹配方法作为“预提升”参数传递给 filterPick:

import re

def filterPick(list,filter):
    return [ ( l, m.group(1) ) for l in list for m in (filter(l),) if m]

theList = ["foo", "bar", "baz", "qurx", "bother"]
searchRegex = re.compile('(a|r$)').search
x = filterPick(theList,searchRegex)

>> [('bar', 'a'), ('baz', 'a'), ('bother', 'r')]

【问题讨论】:

    标签: python regex list-comprehension


    【解决方案1】:
    [m.group(1) for l in lines for m in [regex.search(l)] if m]
    

    “技巧”是for m in [regex.search(l)] 部分——这就是你如何在列表理解中“分配”一个需要多次使用的值——只添加这样一个子句,对象“迭代”在包含您要“分配”给它的一个值的单项列表上。有些人认为这在风格上是可疑的,但我有时觉得它很实用。

    【讨论】:

    • 亚历克斯,我喜欢这样;谢谢和+1。我对这段代码有一些相当繁重的工作 - 我是否应该担心设置和拆除“人造迭代器”的额外开销?顺便说一句,我赞同“以后优化”的教义。
    • @Brent,“虚假迭代器”在搜索调用中应该可以忽略不计;一个小的优化是使用(regex.search(l),) 代替[regex.search(l)](我发现它更具可读性,但速度稍慢——我认为你不可能着急,因为你实际上是从模块而不是 re 对象的方法。将regex.search 作为 listcomp 之外的绑定方法是另一个较小但有用的优化,顺便说一句。
    • 一看到您的回答,我就意识到使用 re.search 并不是最好的方法。您能否为我澄清一下您将如何“[将] regex.search 作为 listcomp 之外的绑定方法”?非常感谢您对 listcomp 和 Python noob 的耐心等待。
    • @Brent, src=regex.search; lst=[m.group(1) for l in lines for m in [src(l)] if m] 是“绑定方法提升”优化(方法查找一次而不是为每一行重做一次——Python 不会在您需要时为您提升属性查找但是,您可以手动进行这种优化,正如我刚刚展示的那样)。
    • @AlexMartelli,我发现嵌套理解比“double for hack”更具可读性:search = re.compile('...').search; out = [m.group(1) for m in map(search, lines) if m] 您可以使用嵌套括号,但在这种情况下 map() 也一样可读,因为Transformer 是一个简单的可调用对象,它实际上更快! (比使用元组的 hack 快 33%,比使用列表的 hack 快 40%,比使用括号的嵌套列表理解快 15%。)使用中等复杂的正则表达式 '(a.*b.*c)' 测量,即 O(n²),在一个巨大的文件名列表中。
    【解决方案2】:
    return [m.group(1) for m in (re.search(regex, l) for l in lines) if m]
    

    【讨论】:

      【解决方案3】:

      可以缩短一点

      def filterPick(lines, regex):
          matches = map(re.compile(regex).match, lines)
          return [m.group(1) for m in matches if m]
      

      您可以将所有内容放在一行中,但这意味着您必须将每一行匹配两次,这会降低效率。

      【讨论】:

      • 不,不需要每行匹配两次,看我的回答。
      • 确实,你的答案更清晰,我 +1 :)
      【解决方案4】:

      Python 3.8 开始,并引入assignment expressions (PEP 572):= 运算符),可以在列表解析中使用局部变量以避免多次调用同一个表达式:

      # items = ["foo", "bar", "baz", "qurx", "bother"]
      [(x, match.group(1)) for x in items if (match := re.compile('(a|r$)').search(x))]
      # [('bar', 'a'), ('baz', 'a'), ('bother', 'r')]
      

      这个:

      • re.compile('(a|r$)').search(x) 的评估命名为变量match(即NoneMatch 对象)
      • 使用此 match 命名表达式(NoneMatch)过滤掉不匹配的元素
      • 并通过提取第一组 (match.group(1)) 在映射值中重复使用 match

      【讨论】:

        【解决方案5】:
        >>> "a" in "a visit to the dentist" 
        True 
        >>> "a" not in "a visit to the dentist" 
        False
        

        这也适用于您在列表中搜索的搜索查询

        `P='a', 'b', 'c'

        'b' in P` 返回真

        【讨论】:

        • 这如何回答这个问题?
        • 这个问题可能提供了一种比 re 检查输入到列表中的更好的方法,但顺便说一下,如果你不想 grep 结果,那就行不通了。 U 总是可以围绕 re 输出做一个简单的循环。手动执行与使用执行相同操作的函数没有太大区别...
        猜你喜欢
        • 2015-06-05
        • 1970-01-01
        • 2014-07-10
        • 2018-08-19
        • 2021-05-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多