Tatsächlich ist die akzeptierte Antwort nicht wahr (oder zumindest nicht mehr, wenn überhaupt wahr), insbesondere sind die folgenden Aussagen falsch:
(1) Der Unterschied besteht nur darin, dass für die Zeile in der Sitzung.Query (Modell1) das ORM für jede Zeile ausgeführt wird, wenn es Ihnen übergeben wird, während für die Zeile in der Sitzung.Query (Modell1) .all () die Funktion ausführt ORM arbeitet an allen Zeilen, bevor Sie sie Ihnen geben.
SQLAlchemy ordnet immer alle Zeilen ORM zu, unabhängig davon, welche der beiden Optionen Sie verwenden. Dies kann in ihrem Quellcode innerhalb dieser Zeilen gesehen werden ; Die loading.instances
Methode gibt zwar einen Generator zurück, aber eine der bereits zugeordneten ORM-Instanzen. Sie können dies im eigentlichen Generatorschleifencode bestätigen :
for row in rows:
yield row
Bis der erste Lauf des Generators beendet ist und eine Instanz ausgegeben wurde, wurden alle Instanzen ORM-zugeordnet. (Nachstehend beispielhaft dargestellt (1))
(2) Der praktische Unterschied besteht darin, dass erstere Ihnen Zeilen geben können, sobald ihre Daten eingegangen sind, und die DB-Ergebnismenge mit weniger Speicherbedarf und Latenz „streamen“.
Wie oben erläutert, ist dies auch falsch, da alle aus der Datenbank abgerufenen Daten verarbeitet / zugeordnet werden, bevor eine Nachgiebigkeit erfolgt.
Die Kommentare sind auch irreführend. Speziell:
Der Datenbanktreffer ist möglicherweise derselbe. Beachten Sie jedoch, dass alle die gesamte Ergebnismenge in den Speicher laden. Dies können Gigabyte an Daten sein
Die gesamte Ergebnismenge wird unabhängig von der Verwendung geladen .all()
oder nicht. In dieser Zeile deutlich zu sehen . Dies ist auch sehr einfach zu testen / zu verifizieren.
Der einzige Unterschied, den ich aus den beiden angegebenen Optionen erkennen kann, besteht darin, dass Sie bei Verwendung .all
zweimal die Ergebnisse durchlaufen (wenn Sie alle Instanzen / Zeilen verarbeiten möchten), da der Generator zuerst durch den Aufruf iteriert / erschöpft wird, in den list(self)
er transformiert werden soll eine Liste.
Da der SQLAlchemy-Code nicht leicht zu verdauen ist, habe ich einen kurzen Ausschnitt geschrieben, um all dies zu veranschaulichen:
class Query:
def all(self):
return list(self)
def instances(self, rows_to_fetch=5):
"""ORM instance generator"""
mapped_rows = []
for i in range(rows_to_fetch):
mapped_rows.append(i)
print("orm work finished for all rows")
for row in mapped_rows:
print("yield row")
yield row
def __iter__(self):
return self.instances()
query = Query()
print("(1) Generator scenario:")
print("First item of generator: ", next(iter(query)))
print("\n(2) List scenario:")
print("First item of list: ", query.all()[0])
"""
RESULTS:
--------
(1) Generator scenario:
orm work finished for all rows
yield row
First item of generator: 0
(2) List scenario:
orm work finished for all rows
yield row
yield row
yield row
yield row
yield row
First item of list: 0
"""
Um eine feinere Kontrolle bei der Verarbeitung großer Ergebnismengen zu haben, müsste beispielsweise so etwas wie Yield_per verwendet werden, und dennoch wäre dies kein Einzelszenario , sondern die ORM-Zuordnung nach mehreren Instanzen.
Erwähnenswert ist der einzige Kommentar , der tatsächlich zutreffend war und leicht übersehen werden kann (daher das Schreiben dieser Antwort) und auf eine Erklärung von Michael Bayer hinweist .
__iter__
tut alle Ergebnisse Prefetch (aus gutem Grund), aber dieses Verhalten kann geändert werden , wenn Sie wissen , was Sie tun.