Pregunta

Tengo una lista de nombres de archivo de biblioteca que necesito para filtrar contra la expresión regular y luego extraer el número de versión de los que coinciden. Esta es la manera obvia de hacerlo:

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))

Esto produce la siguiente lista:

['3.3.1', '3.2.0']

Sin embargo, siento que bucle no es muy 'estilo de Python' y siento que debería ser posible reemplazar 'para' bucle de arriba con un poco inteligente de una sola línea. Sugerencias?

¿Fue útil?

Solución

¿Qué tal una lista por comprensión?

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']

Otros consejos

Uno más de una sola línea sólo para mostrar otras formas (También he limpiado un poco de expresiones regulares):

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

Pero tenga en cuenta, que su versión original es más legible que todas las sugerencias. ¿Vale la pena cambiar?

Usted puede hacer esto:

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

No creo que sea muy fácil de leer, aunque ...

Tal vez sea más clara realiza en dos pasos:

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

No hay nada que no es Pythonic sobre el uso de un estándar para el bucle. Sin embargo, se puede utilizar el mapa href="http://www.go4expert.com/forums/showthread.php?t=6251" rel="nofollow noreferrer"> función para generar una nueva lista en función de los resultados de ejecutar una función contra cada elemento de la lista.

que en realidad no tiene que molestarse con expresiones regulares para su caso simple

>>> 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
>>>

Hacer comprobar más lejos de conseguir los que tienen "puntos".

¿Qué tal este:

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']

Una forma de lo que podía pensar era combinar 'mapa' y la lista de comprensión.
La solución se ve de la siguiente manera:

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]  

A partir Python 3.8, y la introducción de expresiones de asignación (PEP 572) (operador :=), es posible utilizar una variable local dentro de una lista por comprensión con el fin de evitar llamar dos veces el resultado de la coincidencia de expresiones regulares:

# 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']

Este:

  • Nombres la evaluación de pattern.match(lib) como match variable (que es o bien None o un objeto re.Match)
  • utiliza esta match llamado expresión en su lugar (ya sea None o una Match) para filtrar los elementos no coinciden
  • Y reutiliza match en el valor asignado mediante la extracción del primer grupo (match.group(1)).
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top