Choisir entre len()
y count()
dépend de la situation et il est utile de bien comprendre leur fonctionnement pour les utiliser correctement.
Permettez-moi de vous présenter quelques scénarios :
- (le plus crucial) Lorsque vous voulez seulement connaître le nombre d'éléments et que vous ne prévoyez pas de les traiter d'une quelconque manière, il est crucial d'utiliser
count()
:
DO : queryset.count()
- ce qui permettra d'effectuer un seul SELECT COUNT(*) FROM some_table
tous les calculs sont effectués du côté du SGBDR, Python n'a plus qu'à récupérer le numéro de résultat avec un coût fixe de O(1).
NE LE FAITES PAS : len(queryset)
- cela permettra de réaliser SELECT * FROM some_table
requête, en récupérant la table entière O(N) et en nécessitant O(N) de mémoire supplémentaire pour la stocker. C'est le pire qu'on puisse faire
- Lorsque vous avez l'intention de récupérer le queryset de toute façon, il est légèrement préférable d'utiliser
len()
ce qui n'entraînera pas de requête supplémentaire dans la base de données, comme c'est le cas pour count()
serait
len()
(une seule requête de la base de données)
len(queryset) # SELECT * fetching all the data - NO extra cost - data would be fetched anyway in the for loop
for obj in queryset: # data is already fetched by len() - using cache
pass
count()
(deux requêtes de la base de données !) :
queryset.count() # First db query SELECT COUNT(*)
for obj in queryset: # Second db query (fetching data) SELECT *
pass
-
Rétablissement du deuxième cas (lorsque le queryset a déjà été récupéré) :
for obj in queryset: # iteration fetches the data
len(queryset) # using already cached data - O(1) no extra cost
queryset.count() # using cache - O(1) no extra db query
len(queryset) # the same O(1)
queryset.count() # the same: no query, O(1)
Tout sera clair une fois que vous aurez jeté un coup d'œil "sous le capot" :
class QuerySet(object):
def __init__(self, model=None, query=None, using=None, hints=None):
# (...)
self._result_cache = None
def __len__(self):
self._fetch_all()
return len(self._result_cache)
def _fetch_all(self):
if self._result_cache is None:
self._result_cache = list(self.iterator())
if self._prefetch_related_lookups and not self._prefetch_done:
self._prefetch_related_objects()
def count(self):
if self._result_cache is not None:
return len(self._result_cache)
return self.query.get_count(using=self.db)
Bonnes références dans la documentation de Django :