@ Radim,
Einige Punkte haben mich in diesem Thread verwirrt. Ich habe bemerkt, dass ich das Grundlegende nicht verstanden habe: Was war Ihr Problem.
Jetzt denke ich, dass ich verstanden habe und ich wünsche Ihnen, dies zu bestätigen.
Ich werde Ihren Code so darstellen
import itertools
def grouper(iterable, chunksize):
i = iter(iterable)
while True:
chunk = list(itertools.islice(i, int(chunksize)))
if not chunk:
break
yield chunk
............
............
gigi = grouper(an_iterable,4)
# vor A
# A = grouper(an_iterable,4)
# korrigiert:
A = gigi.next()
# nach A
................
...........
# ein Objekt x aus A ableiten; x verbraucht nicht viel Speicher
............
# Löschen von A, da es viel Speicher verbraucht:
del A
# Code läuft weiter, braucht Zeit, um ausgeführt zu werden
................
................
......
..........
# vor B
# B = grouper(an_iterable,4)
# korrigiert:
B = gigi.next()
# nach B
.....................
........
Ihr Problem ist, dass selbst während der Zeit zwischen
# nach dem Löschen von A, Code läuft weiter, braucht Zeit, um ausgeführt zu werden
und
# vor B,
das Objekt mit dem gelöschten Namen 'A' immer noch existiert und viel Speicher verbraucht, weil es noch eine Bindung zwischen diesem Objekt und dem Bezeichner 'chunk' innerhalb der Generatorfunktion gibt ?
Entschuldigen Sie, dass ich Sie nach diesem jetzt offensichtlichen Punkt frage.
Da es jedoch eine gewisse Verwirrung im Thread gab, möchte ich, dass Sie bestätigen, dass ich Ihr Problem jetzt richtig verstanden habe.
.
@ phihag
Sie haben in einem Kommentar geschrieben:
1)
Nach dem yield chunk
gibt es keine Möglichkeit, auf den Wert zuzugreifen der in chunk gespeichert ist. Diese Funktion hält daher keine Referenzen auf das betreffende Objekt
(Übrigens, ich hätte nicht deshalb geschrieben, sondern 'weil')
Ich denke, dass diese Behauptung #1 diskutabel ist.
Tatsächlich bin ich überzeugt, dass sie falsch ist. Aber es gibt eine Feinheit in dem, was Sie behaupten, nicht nur in diesem Zitat allein, sondern insgesamt, wenn wir auch berücksichtigen, was Sie am Anfang Ihrer Antwort sagen.
Lassen Sie uns alles ordnen.
Der folgende Code scheint das Gegenteil Ihrer Behauptung zu beweisen "Nach dem yield chunk gibt es keine Möglichkeit, auf den Wert zuzugreifen, der in chunk gespeichert ist, von dieser Funktion aus."
import itertools
def grouper(iterable, chunksize):
i = iter(iterable)
chunk = ''
last = ''
while True:
print 'neue Runde ',id(chunk)
if chunk:
last = chunk[-1]
chunk = list(itertools.islice(i, int(chunksize)))
print 'neuer Chunk ',id(chunk),' Länge des Chunks:',len(chunk)
if not chunk:
break
yield '%s - %s' % (last,' , '.join(chunk))
print 'Ende der Runde',id(chunk),'\n'
for x in grouper(['1','2','3','4','5','6','7','8','9','10','11'],'4'):
print repr(x)
Ergebnis
neue Runde 10699768
neuer Chunk 18747064 Länge des Chunks: 4
' - 1 , 2 , 3 , 4'
Ende der Runde 18747064
neue Runde 18747064
neuer Chunk 18777312 Länge des Chunks: 4
'4 - 5 , 6 , 7 , 8'
Ende der Runde 18777312
neue Runde 18777312
neuer Chunk 18776952 Länge des Chunks: 3
'8 - 9 , 10 , 11'
Ende der Runde 18776952
neue Runde 18776952
neuer Chunk 18777512 Länge des Chunks: 0
.
Sie haben jedoch auch geschrieben (es ist der Anfang Ihrer Antwort):
2)
Nach yield chunk
wird der Variablenwert in der Funktion nie wieder verwendet, daher wird ein guter Interpreter/Garbage Collector den chunk bereits für die Aufforderung zur Garbage Collection freigeben (Anmerkung: cpython 2.7 scheint dies nicht zu
Diesmal sagen Sie nicht, dass die Funktion nach yield chunk
keine Referenz mehr auf chunk hält, sondern dass ihr Wert vor der Erneuerung von chunk in der nächsten Runde der while
-Schleife nicht mehr verwendet wird. Das stimmt, im Code von Radim wird das Objekt chunk nicht wieder verwendet, bevor der Bezeichner 'chunk' in der Anweisung chunk = list(itertools.islice(i, int(chunksize)))
in der nächsten Runde der Schleife neu zugewiesen wird.
.
Diese Behauptung #2 in diesem Zitat, anders als die vorherige Behauptung #1, hat zwei logische Konsequenzen:
ERSTENS, mein obiger Code kann nicht strikt beweisen, dass es tatsächlich einen Weg gibt, auf den Wert von chunk zuzugreifen, nach der Anweisung yield chunk
, für jemanden, der so denkt wie Sie.
Weil die Bedingungen in meinem obigen Code nicht den Bedingungen entsprechen, unter denen Sie das Gegenteil behaupten, das heißt: im Code von Radim, über den Sie sprechen, wird das Objekt chunk tatsächlich nicht mehr verwendet, bevor die nächste Runde beginnt.
Deshalb kann man behaupten, dass es wegen der Verwendung von chunk in meinem oben genannten Code (die Anweisungen print 'Ende der Runde',id(chunk),'\n'
, print 'neue Runde ',id(chunk)
und last = chunk[-1]
verwenden es), dass eine Referenz auf das Objekt chunk noch nach dem yield chunk
vorhanden ist.
ZWEITENS, wenn man weiterdenkt, führt die Kombination Ihrer beiden Zitate dazu, dass Sie denken, dass es daran liegt, dass chunk nach der yield chunk
Anweisung im Code von Radim nicht mehr verwendet wird, dass keine Referenz darauf gehalten wird.
Es ist eine Frage der Logik, meiner Meinung nach: das Fehlen einer Referenz zu einem Objekt ist die Bedingung für seine Freigabe, daher wenn Sie behaupten, dass der Speicher vom Objekt befreit wird, weil es nicht mehr verwendet wird, ist es gleichbedeutend mit der Behauptung, dass der Speicher vom Objekt befreit wird, weil seine Arbeitslosigkeit den Interpreter dazu bringt, die Referenz darauf in der Funktion zu löschen.
Ich fasse zusammen:
Sie behaupten, dass in Radims Code chunk nach yield chunk
nicht mehr verwendet wird, dann keine Referenz mehr darauf gehalten wird, dann ..... cpython 2.7 wird es nicht tun... aber pypy 1.6 mit standardmäßigem gc befreit den Speicher vom Objekt chunk.
Ich bin an diesem Punkt sehr überrascht über die Schlussfolgerung, und der Grund, warum ich all dem nicht zustimme, ist, dass es impliziert, dass pypy 1.6 in der Lage wäre, den Code zu analysieren und zu erkennen, dass chunk nach der Anweisung yield chunk
nicht mehr verwendet wird im Code von Radim, dass keine Referenz aufrecht erhalten wird.
Es wäre meiner Meinung nach nicht klar ausgedrückt wie von Ihnen, aber ohne diesen Gedanken fände ich Ihre Behauptungen in den beiden Zitaten unlogisch und unverständlich.
Was mich an diesem Schluss verwirrt und der Grund, warum ich all dem nicht zustimme, ist, dass es impliziert, dass pypy 1.6 in der Lage wäre, den Code zu analysieren und zu erkennen, dass chunk nach der Anweisung yield chunk
nicht mehr verwendet wird ein Restreferenz innerhalb der Generatorfunktion besteht. Haben Sie ein ähnliches Verhalten bei pypy 1.6 beobachtet? Ich sehe keinen anderen Weg, um die verbleibende Referenz innerhalb des Generators offenzulegen, da laut Ihrem Zitat #2 jede Verwendung von chunk nach yield chunk
ausreicht, um die Aufrechterhaltung einer Referenz darauf auszulösen. Es ist ein Problem ähnlich wie in der Quantenmechanik: die Tatsache, dass die Geschwindigkeit einer Teilchens gemessen wird, verändert seine Geschwindigkeit.....
Was mich an diesem Schluss verwirrt und der Grund, warum ich all dem nicht zustimme, ist, dass es impliziert, dass pypy 1.6 in der Lage wäre, den Code zu analysieren und zu erkennen, dass chunk nach der Anweisung yield chunk
nicht mehr verwendet wird ein Restreferenz innerhalb der Generatorfunktion besteht. Haben Sie ein ähnliches Verhalten bei pypy 1.6 beobachtet? Ich sehe keinen anderen Weg, um die verbleibende Referenz innerhalb des Generators offenzulegen, da laut Ihrem Zitat #2 jede Verwendung von chunk nach yield chunk
ausreicht, um die Aufrechterhaltung einer Referenz darauf auszulösen. Es ist ein Problem ähnlich wie in der Quantenmechanik: die Tatsache, dass die Geschwindigkeit einer Teilchens gemessen wird, verändert seine Geschwindigkeit.....
-
Erklären Sie bitte, was Sie genau zu all dem denken. Wo liege ich falsch in meinem Verständnis Ihrer Ideen?
-
Sagen Sie, ob Sie einen Beweis dafür haben, dass zumindest pypy 1.6, keine Referenz auf chunk hält, wenn es nicht mehr verwendet wird. Was das Problem des ursprünglichen Codes von Radim war, war, dass der Speicher durch die Persistenz des Objekts chunk aufgrund seiner noch vorhandenen Referenz innerhalb der Generatorfunktion zu stark verbraucht wurde: das war ein indirektes Symptom für die Existenz einer solchen persistenten Referenz innerhalb. Haben Sie ein ähnliches Verhalten mit pypy 1.6 beobachtet? Ich sehe keinen anderen Weg, um die verbleibende Referenz innerhalb des Generators offenzulegen, da laut Ihrem Zitat #2, jede Verwendung von chunk nach yield chunk
5 Stimmen
Häufig möchten Personen mit anderen Sprachhintergründen die Speicherzuweisung und -freigabe genau kontrollieren. Tatsächlich wird in den meisten anderen Sprachen die meiste Arbeit genau darauf ausgerichtet. Widerstehen Sie der Versuchung, Vermutungen über den Speichereffizienz eines bestimmten Python-Konstrukts anzustellen: Die Speicherverwaltung ist zu wichtig, um sie dem Programmierer zu überlassen :p
0 Stimmen
Leider brauche ich den so verschwendeten Speicher. Die Chunks sind groß. Im Moment verwende ich einen Workaround (das Schlaufenheben in den lokalen Bereich des Aufrufers, d. h. ohne eine separate Generierungsfunktion), aber ich frage mich, ob es einen besseren/saubereren Weg gibt.
1 Stimmen
Wenn Sie wirklich diese Art von Kontrolle brauchen, dann verwenden Sie die falsche Sprache. Aber in meiner Erfahrung denken die überwiegende Mehrheit der Personen, die glauben, dass sie diese Art von Kontrolle benötigen, wirklich nicht.
1 Stimmen
@Karl Knechtel: Es ist ein verbreiteter Irrtum, dass Python irgendwie keine großen Daten handhaben kann. Tatsächlich ist Python ziemlich gut darin - gerade wegen seiner eingebauten Unterstützung für Generatoren und Co-Routinen.
0 Stimmen
Ich habe nichts über den Umgang mit großen Daten gesagt. Ich habe über Speicherverwaltung gesprochen. Generatoren und Co-Routinen können Sie nur bis zu einem gewissen Punkt bringen, besonders wenn Sie planen, ihre Ausgabe mit einer großen
chunksize
zusammenzufassen.0 Stimmen
@Radim Bitte sieh dir die Frage an, die ich im Beitrag gestellt habe. (Kommentare von anderen sind ebenfalls willkommen)
0 Stimmen
Der Teil, der in der Frage und in allen Antworten fehlt, sind Speichermessungen, die zeigen, ob die Lösungen überhaupt Auswirkungen auf den tatsächlichen Speicherverbrauch haben Beispiel.