Voici trois possibilités:
foo = """
this is
a multi-line string.
"""
def f1(foo=foo): return iter(foo.splitlines())
def f2(foo=foo):
retval = ''
for char in foo:
retval += char if not char == '\n' else ''
if char == '\n':
yield retval
retval = ''
if retval:
yield retval
def f3(foo=foo):
prevnl = -1
while True:
nextnl = foo.find('\n', prevnl + 1)
if nextnl < 0: break
yield foo[prevnl + 1:nextnl]
prevnl = nextnl
if __name__ == '__main__':
for f in f1, f2, f3:
print list(f())
L'exécution de ce que le script principal, confirme les trois fonctions sont équivalentes. Avec timeit
(et un * 100
pour foo
pour obtenir substantielle des chaînes pour une mesure plus précise):
$ python -mtimeit -s'import asp' 'list(asp.f3())'
1000 loops, best of 3: 370 usec per loop
$ python -mtimeit -s'import asp' 'list(asp.f2())'
1000 loops, best of 3: 1.36 msec per loop
$ python -mtimeit -s'import asp' 'list(asp.f1())'
10000 loops, best of 3: 61.5 usec per loop
Note nous avons besoin de l' list()
appel pour s'assurer les itérateurs sont parcourus, pas seulement construit.
OIE, de la naïveté de la mise en œuvre est beaucoup plus rapide, il n'est même pas drôle: 6 fois plus rapide que ma tentative avec find
des appels, qui à son tour est 4 fois plus rapide qu'un faible niveau d'approche.
Leçons à retenir: la mesure est toujours une bonne chose (mais doit être exacte); string méthodes comme splitlines
sont mis en œuvre très rapide; de la mise chaînes par programmation à un niveau très faible (de l'esp. par des boucles d' +=
de très petits morceaux) peut être assez lente.
Edit: ajout de @Jacob proposition, légèrement modifié afin de donner les mêmes résultats que les autres (les espaces à droite sur une ligne, sont conservés), c'est à dire:
from cStringIO import StringIO
def f4(foo=foo):
stri = StringIO(foo)
while True:
nl = stri.readline()
if nl != '':
yield nl.strip('\n')
else:
raise StopIteration
La mesure donne:
$ python -mtimeit -s'import asp' 'list(asp.f4())'
1000 loops, best of 3: 406 usec per loop
pas tout à fait aussi bon que l' .find
approche fondée sur -- encore, la peine de garder à l'esprit, car elle pourrait être moins enclins à petit tout-en-un bugs (une boucle où vous pouvez voir les occurrences de +1 et -1, comme mon f3
- dessus, devrait déclencher automatiquement tout-en-un soupçons -- et de nombreuses boucles qui manquent de ces réglages et devrait disposer d'eux-même si je crois que mon code est aussi la droite depuis que j'ai été en mesure de vérifier sa sortie avec d'autres fonctions).
Mais le split-approche fondée sur les règles.
Aparté: peut-être un meilleur style pour f4
serait:
from cStringIO import StringIO
def f4(foo=foo):
stri = StringIO(foo)
while True:
nl = stri.readline()
if nl == '': break
yield nl.strip('\n')
au moins, c'est un peu moins verbeux. La nécessité de la bande de fuite \n
s malheureusement interdit la plus claire et la plus rapide de remplacement de l' while
boucle avec return iter(stri)
( iter
partie de quoi est redondante dans les versions modernes de Python, je crois que depuis 2.3 ou 2.4, mais c'est aussi inoffensif). Peut-être la peine d'essayer, aussi:
return itertools.imap(lambda s: s.strip('\n'), stri)
ou des variations de ceux-ci-mais j'arrête ici, puisque c'est à peu près un exercice théorique wrt l' strip
base, la plus simple et la plus rapide, un.