QuerySet
Was ist in Django die beste Option zum Zählen der Objekte , da ich eine habe , über die ich iterieren und die Ergebnisse drucken werde? len(qs)
oder qs.count()
?
(Auch da das Zählen der Objekte in derselben Iteration keine Option ist.)
QuerySet
Was ist in Django die beste Option zum Zählen der Objekte , da ich eine habe , über die ich iterieren und die Ergebnisse drucken werde? len(qs)
oder qs.count()
?
(Auch da das Zählen der Objekte in derselben Iteration keine Option ist.)
Antworten:
Obwohl die Django-Dokumente die Verwendung empfehlen, count
anstatt len
:
Hinweis: Verwenden Sie diese
len()
Option nicht für QuerySets, wenn Sie lediglich die Anzahl der Datensätze im Set bestimmen möchten. Es ist viel effizienter, eine Zählung auf Datenbankebene mithilfe von SQL durchzuführenSELECT COUNT(*)
, und Django bietetcount()
aus genau diesem Grund eine Methode.
Da Sie dieses QuerySet ohnehin iterieren, wird das Ergebnis zwischengespeichert (sofern Sie es nicht verwenden iterator
). Daher ist es vorzuziehen, es zu verwenden len
, da dies verhindert, dass die Datenbank erneut aufgerufen wird und möglicherweise auch eine andere Anzahl von Ergebnissen abgerufen wird !) .
Wenn Sie verwenden iterator
, würde ich aus den gleichen Gründen vorschlagen, eine Zählvariable einzuschließen, während Sie durchlaufen (anstatt count zu verwenden).
Die Wahl zwischen len()
und count()
hängt von der jeweiligen Situation ab. Es lohnt sich, genau zu verstehen, wie sie funktionieren, um sie richtig zu verwenden.
Lassen Sie mich einige Szenarien vorstellen:
(am wichtigsten) Wenn Sie nur die Anzahl der Elemente wissen möchten und nicht vorhaben, sie in irgendeiner Weise zu verarbeiten, ist es wichtig, Folgendes zu verwenden count()
:
DO: queryset.count()
- Dies führt eine einzelne SELECT COUNT(*) some_table
Abfrage durch. Alle Berechnungen werden auf der RDBMS-Seite ausgeführt. Python muss nur die Ergebnisnummer mit den festen Kosten von O (1) abrufen.
NICHT: len(queryset)
- Dies führt eine SELECT * FROM some_table
Abfrage durch, ruft die gesamte Tabelle O (N) ab und benötigt zusätzlichen O (N) -Speicher zum Speichern. Dies ist das Schlimmste, was getan werden kann
Wenn Sie das Abfrageset trotzdem abrufen möchten, ist es etwas besser zu verwenden len()
, da dies keine zusätzliche Datenbankabfrage verursachtcount()
dies wäre:
len(queryset) # 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
Anzahl:
queryset.count() # this will perform an extra db query - len() did not
for obj in queryset: # fetching data
pass
2. Fall rückgängig gemacht (wenn der Abfragesatz bereits abgerufen wurde):
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)
Alles wird klar, wenn Sie einen Blick "unter die Haube" werfen:
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)
Gute Referenzen in Django-Dokumenten:
QuerySet
kontextbezogene Veröffentlichung der Implementierung.
Ich denke, die Verwendung len(qs)
ist hier sinnvoller, da Sie die Ergebnisse durchlaufen müssen.qs.count()
ist eine bessere Option, wenn alles, was Sie tun möchten, die Anzahl druckt und nicht über die Ergebnisse iteriert.
len(qs)
wird die Datenbank mit treffen, select * from table
während die Datenbank mit qs.count()
treffen wirdselect count(*) from table
.
Außerdem qs.count()
wird return integer angegeben, und Sie können nicht darüber iterieren
Für Personen, die Testmessungen bevorzugen (Postresql):
Wenn wir ein einfaches Personenmodell und 1000 Instanzen davon haben:
class Person(models.Model):
name = models.CharField(max_length=100)
age = models.SmallIntegerField()
def __str__(self):
return self.name
Im Durchschnitt gibt es:
In [1]: persons = Person.objects.all()
In [2]: %timeit len(persons)
325 ns ± 3.09 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
In [3]: %timeit persons.count()
170 ns ± 0.572 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
Wie können Sie also count()
fast zweimal schneller sehen als len()
in diesem speziellen Testfall?
Zusammenfassen, was andere bereits beantwortet haben:
len()
ruft alle Datensätze ab und iteriert darüber.count()
führt eine SQL COUNT-Operation aus (viel schneller bei großen Abfragesätzen).Es ist auch richtig, dass, wenn nach dieser Operation der gesamte Abfragesatz iteriert wird, die Verwendung insgesamt etwas effizienter sein könnte len()
.
jedoch
In einigen Fällen, beispielsweise bei Speicherbeschränkungen, kann es zweckmäßig sein (wenn möglich), die ausgeführte Operation auf die Datensätze aufzuteilen. Dies kann durch Django-Paginierung erreicht werden .
Dann count()
wäre die Verwendung die Wahl, und Sie könnten vermeiden, den gesamten Abfragesatz auf einmal abrufen zu müssen.