Ответ 1
Как насчет понимания списка?
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']
У меня есть список имен файлов библиотек, которые мне нужно отфильтровать от регулярного выражения, а затем извлечь номер версии из тех, которые соответствуют. Это очевидный способ сделать это:
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" выше на некоторый интеллектуальный однострочный. Предложения?
Как насчет понимания списка?
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']
Еще один однострочный экран, чтобы показать другие способы (я также немного очистил регулярное выражение):
regex = re.compile(r'^libIce\.so\.([0-9]+\.[0-9]+\.[0-9]+)$')
sum(map(regex.findall, libs), [])
Но обратите внимание, что ваша оригинальная версия более читаема, чем все предложения. Стоит ли меняться?
Вы можете сделать это:
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]
Нет ничего, что не является питоническим в использовании стандарта для цикла. Однако вы можете использовать функцию map() для создания нового списка на основе результатов от функции, выполняемой против каждого элемента в списке.
вам действительно не нужно беспокоиться о регулярном выражении для вашего простого случая
>>> 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
>>>
Сделайте еще одну проверку, чтобы получить те, у которых есть "точки".
Как насчет этого:
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']
Один из способов, которым я мог подумать, состоял в том, чтобы объединить "карту" и список. Решение выглядит следующим образом:
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]
Начиная с 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)
).