列表过滤和转换

10

我有一个库文件名列表,需要使用正则表达式进行筛选,然后从匹配的文件名中提取版本号。这是显而易见的方法:

libs = ['libIce.so.33', 'libIce.so.3.3.1', 'libIce.so.32', 'libIce.so.3.2.0']
versions = []
regex = re.compile('libIce.so\.([0-9]+\.[0-9]+\.[0-9]+)')
for l in libs:
    m = regex.match(l)
    if m:
        versions.append(m.group(1))

这会产生以下列表:

['3.3.1', '3.2.0']

我感觉循环不太符合 Python 的风格,应该有可能用一行代码替换上面的 'for' 循环。 有什么建议吗?

8个回答

19

怎么样用列表推导式?

In [5]: versions = [m.group(1) for m in [regex.match(lib) for lib in libs] if m] 
In [6]: versions
Out[6]: ['3.3.1', '3.2.0']

8

这里还有一个一行代码的示例,展示了其他的实现方式(我也稍微清理了正则表达式):

regex = re.compile(r'^libIce\.so\.([0-9]+\.[0-9]+\.[0-9]+)$')
sum(map(regex.findall, libs), [])

但请注意,您原来的版本比所有建议都更易读。是否值得更改?

1
感谢您使用“findall”和“sum”! 关于可读性 - 我已经习惯了所有的STL和Boost算法 :) - Aleksei Potov
由于某种原因,这个答案对我来说比被接受/赞同的答案更有意义。 - gloomy.penguin

5
你可以这样做:
versions = [m.group(1) for m in [regex.match(l) for l in libs] if m]

尽管如此,我认为它的可读性不高...

也许分成两步会更加清晰:

matches = [regex.match(l) for l in line]
versions = [m.group(1) for m in matches if m]

1

使用标准的for循环并没有什么不符合Python风格的地方。但是,你可以使用map()函数根据对列表中每个项目运行的函数的结果生成一个新列表。


0

对于你的简单情况,你实际上不需要费心去使用正则表达式。

>>> libs = ['libIce.so.33', 'libIce.so.3.3.1', 'libIce.so.32', 'libIce.so.3.2.0']
>>> libs
['libIce.so.33', 'libIce.so.3.3.1', 'libIce.so.32', 'libIce.so.3.2.0']
>>> for i in libs:
...   print i.split("so.")
...
['libIce.', '33']
['libIce.', '3.3.1']
['libIce.', '32']
['libIce.', '3.2.0']
>>> for i in libs:
...   print i.split("so.")[-1]
...
33
3.3.1
32
3.2.0
>>>

进一步检查以获取那些带有“点”的内容。


0
这个怎么样?
import re

def matches(regexp, list):
    'Regexp, [str] -> Iterable(Match or None)'
    return (regexp.match(s) for s in list)

libs = ['libIce.so.33', 'libIce.so.3.3.1', 'libIce.so.32', 'libIce.so.3.2.0']
regexp = re.compile('libIce.so\.([0-9]+\.[0-9]+\.[0-9]+)')
versions = [m.group(1) for m in matches(regexp, libs) if m is not None]

>>> print versions
['3.3.1', '3.2.0']

0

我能想到的一种方法是将'map'和列表推导结合起来。
解决方案如下:

import re  
libs = ['libIce.so.33', 'libIce.so.3.3.1', 'libIce.so.32', 'libIce.so.3.2.0']  
versions = []  

regex = re.compile('libIce.so\.([0-9]+\.[0-9]+\.[0-9]+)')  

def match(s):  
    m = regex.match(s)  
    if m:  
        return m.group(1)  

versions = [x for x in map(match,libs) if x]  


0

Python 3.8 开始,引入了 赋值表达式 (PEP 572) (:= 运算符),可以在列表推导式中使用局部变量,以避免两次调用正则表达式匹配的结果:

# libs = ['libIce.so.33', 'libIce.so.3.3.1', 'libIce.so.32', 'libIce.so.3.2.0']
# pattern = re.compile(r'libIce.so\.([0-9]+\.[0-9]+\.[0-9]+)')
[match.group(1) for lib in libs if (match := pattern.match(lib))]
# ['3.3.1', '3.2.0']

这个代码:

  • pattern.match(lib) 的评估结果命名为变量 match (它是 None 或一个 re.Match 对象)
  • 使用这个被命名的表达式 match 来过滤掉不匹配的元素(无论是 None 还是 Match
  • 并在映射值中重复使用 match,通过提取第一个组来获取值(match.group(1))。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接