2009-11-03 4 views
9

J'ai une liste de noms de fichiers de bibliothèque dont j'ai besoin pour filtrer par rapport à l'expression régulière, puis extraire le numéro de version de ceux qui correspondent. Ceci est la façon évidente de le faire:Filtrage de liste Python et transformation

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

qui produit la liste suivante:

['3.3.1', '3.2.0'] 

Pourtant, je sens que la boucle est pas très « style Python » et estiment qu'il devrait être possible de remplacer " pour 'boucle ci-dessus avec un seul doublure intelligente. Suggestions?

Répondre

19

Que diriez-vous d'une compréhension de liste?

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

Vous pouvez le faire:

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

Je ne pense pas qu'il est très facile à lire, mais ...

Peut-être qu'il est plus clair fait en deux étapes:

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

vous n'avez pas vraiment besoin de vous embêter avec regex pour votre cas 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 
>>> 

Ne vérifier plus loin pour obtenir ceux avec des « points ».

1

Il n'y a rien de pythonique dans l'utilisation d'une boucle standard. Toutefois, vous pouvez utiliser la fonction map() pour générer une nouvelle liste basée sur les résultats d'une fonction exécutée sur chaque élément de la liste.

0

Que diriez-vous celui-ci:

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

Une façon que je pouvais penser était de combiner « carte » et la compréhension de la liste.
La solution se présente comme ci-dessous:

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] 

8

Encore un one-liner juste pour montrer d'autres moyens (je l'ai aussi nettoyé un peu regexp):

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

Mais attention, que votre version originale est plus lisible que toutes les suggestions. Cela vaut-il la peine de changer?

+1

Merci à la fois pour 'findall' et 'sum'! En ce qui concerne la lisibilité - déjà utilisé avec tous les algorithmes stl et boost :) –

+0

pour une raison qui me fait beaucoup plus de sens que la réponse acceptée/upvoted. –