85 votes

Existe-t-il une méthode intégrée pour obtenir la longueur d'un itérable en Python ?

Par exemple, les fichiers, en Python, sont itérables - ils itèrent sur les lignes du fichier. Je veux compter le nombre de lignes.

Voici un moyen rapide d'y parvenir :

lines = len(list(open(fname)))

Cependant, cette opération charge tout le fichier en mémoire (en une seule fois). Cela va à l'encontre de l'objectif d'un itérateur (qui ne doit conserver que la ligne courante en mémoire).

Cela ne fonctionne pas :

lines = len(line for line in open(fname))

car les générateurs n'ont pas de longueur.

Existe-t-il un moyen d'y parvenir sans définir une fonction de comptage ?

def count(i):
    c = 0
    for el in i: c += 1
    return c

Pour clarifier, je comprends que le fichier entier devra être lu ! C'est juste que je ne veux pas qu'il soit en mémoire en une seule fois.

8voto

Triptych Points 70247

Absolument pas, pour la simple raison que les itérables ne sont pas garantis finis.

Prenons l'exemple de cette fonction génératrice parfaitement légale :

def forever():
    while True:
        yield "I will run forever"

En essayant de calculer la longueur de cette fonction avec len([x for x in forever()]) ne fonctionnera manifestement pas.

Comme vous l'avez noté, l'objectif des itérateurs/générateurs est en grande partie de pouvoir travailler sur un grand ensemble de données sans avoir à tout charger en mémoire. Le fait que vous ne puissiez pas obtenir une longueur immédiate doit être considéré comme un compromis.

3voto

ShadowRanger Points 44

La duplication n'ayant apparemment pas été remarquée à l'époque, je vais publier un extrait du document suivant ma réponse au duplicata ici aussi :

Il existe un moyen d'obtenir des résultats nettement plus rapides que ceux de la sum(1 for i in it) lorsque l'itérable peut être long (et pas significativement plus lent lorsque l'itérable est court), tout en conservant un comportement de surcharge de mémoire fixe (contrairement à len(list(it)) ), afin d'éviter les problèmes d'échange et de réaffectation des données pour les entrées plus importantes.

# On Python 2 only, get zip that lazily generates results instead of returning list
from future_builtins import zip

from collections import deque
from itertools import count

def ilen(it):
    # Make a stateful counting iterator
    cnt = count()
    # zip it with the input iterator, then drain until input exhausted at C level
    deque(zip(it, cnt), 0) # cnt must be second zip arg to avoid advancing too far
    # Since count 0 based, the next value is the count
    return next(cnt)

Comme len(list(it)) , ilen(it) exécute la boucle en code C sur CPython ( deque , count y zip sont tous implémentés en C) ; éviter l'exécution d'un code d'octets par boucle est généralement la clé de la performance en CPython.

Plutôt que de répéter ici tous les chiffres relatifs aux performances, je me contenterai de vous indiquer ma réponse avec les détails complets de la perf .

1voto

Kirill Bulygin Points 2562

Pour le filtrage, cette variante peut être utilisée :

sum(is_good(item) for item in iterable)

qui peut être lu naturellement comme "compter les bons articles" et qui est plus court et plus simple (bien que peut-être moins idiomatique) que :

sum(1 for item in iterable if is_good(item)))

Note : Le fait que True s'évalue à 1 dans les contextes numériques est spécifiée dans la documentation ( https://docs.python.org/3.6/library/stdtypes.html#boolean-values ), de sorte que cette coercition n'est pas un hack (contrairement à d'autres langages comme C/C++).

0voto

Nikron Points 373

Si vous y réfléchissez, comment proposez-vous de trouver le nombre de lignes d'un fichier sans lire tout le fichier pour trouver les nouvelles lignes ? Bien sûr, vous pouvez trouver la taille du fichier, et si vous pouvez garantir que la longueur d'une ligne est x, vous pouvez obtenir le nombre de lignes dans un fichier. Mais à moins d'avoir une contrainte quelconque, je ne vois pas du tout comment cela peut fonctionner. De plus, comme les itérables peuvent être infiniment longs...

-1voto

Casey Points 17

J'ai fait un test entre les deux procédures courantes dans un de mes codes, qui calcule le nombre de graphes sur n sommets, pour voir quelle méthode de comptage des éléments d'une liste générée est la plus rapide. Sage dispose d'un générateur graphs(n) qui génère tous les graphes sur n sommets. J'ai créé deux fonctions qui obtiennent la longueur d'une liste obtenue par un itérateur de deux manières différentes et j'ai chronométré chacune d'entre elles (en faisant une moyenne sur 100 essais) à l'aide de la fonction time.time(). Les fonctions sont les suivantes :

def test_code_list(n):
    l = graphs(n)
    return len(list(l))

et

def test_code_sum(n):
    S = sum(1 for _ in graphs(n))
    return S

Maintenant, je chronomètre chaque méthode

import time

t0 = time.time()
for i in range(100):
    test_code_list(5)
t1 = time.time()

avg_time = (t1-t0)/10

print 'average list method time = %s' % avg_time

t0 = time.time()
for i in range(100):
    test_code_sum(5)
t1 = time.time()

avg_time = (t1-t0)/100

print "average sum method time = %s" % avg_time

temps moyen de la méthode des listes = 0,0391882109642

temps moyen de la méthode de la somme = 0,0418473792076

Ainsi, en calculant le nombre de graphes sur n=5 sommets de cette manière, la méthode de la liste est légèrement plus rapide (bien que 100 essais ne constituent pas un échantillon de grande taille). Mais lorsque j'ai augmenté la longueur de la liste calculée en essayant des graphes sur n=7 sommets (c'est-à-dire en remplaçant graphes(5) par graphes(7)), le résultat a été le suivant :

temps moyen de la méthode des listes = 4.14753051996

temps moyen de la méthode de la somme = 3,96504004002

Dans ce cas, la méthode de la somme était légèrement plus rapide. Dans l'ensemble, les deux méthodes sont à peu près aussi rapides l'une que l'autre, mais la différence peut dépendre de la longueur de votre liste (il se peut aussi que je n'aie fait qu'une moyenne de 100 essais, ce qui n'est pas très élevé - cela aurait pris une éternité dans le cas contraire).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X