Lista de filtrado y transformación
-
13-09-2019 - |
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?
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)
comomatch
variable (que es o bienNone
o un objetore.Match
) - utiliza esta
match
llamado expresión en su lugar (ya seaNone
o unaMatch
) para filtrar los elementos no coinciden - Y reutiliza
match
en el valor asignado mediante la extracción del primer grupo (match.group(1)
).