Problem mit Listen

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
mzh
User
Beiträge: 295
Registriert: Dienstag 3. März 2009, 15:27
Wohnort: ZH

Hallo zusammen
Ich habe ein Problem mit etwas, dass eigentlich ganz leicht sein soll. Ich habe zwei Module, particles.py und sampler.py. particles.py enthält einige Funktionen, die das "hard-spheres-collision" model implementieren, sampler.py stellt eine Klasse zur Verfügung, die zur Speicherung der Koordinaten während der Simulation genutzt wird und anschliessend eine kleine Datenbearbeitung durchführt (Verteilung der Partikel über die Simulation berechnen, u.ä.).

Code: Alles auswählen

class Sampler:
    def __init__(self):
        """Initialize variables
        """
        self.count = 0 
        self.data       = []

        #Distributions
        self.partdist   = []
        self.partdisteq = []

    def sampleData(self, X, Y): 
        """Sampling the positions of the particles.
        X and Y are lists.
        self.data[step][coordinate][particle_id]
        """
        self.data.append([X, Y]) 
        self.count += 1

    def getData(self):
        return self.data

Code: Alles auswählen

sampler = Sample()
def simulateNSteps(xpos, ypos, xvel, yvel):
    print 'Simulation'
    #loop over steps.
    for s in range(nstep): 
         [loop over all particles]
            #Updating positions

            xpos[i] += xvel[i]
            ypos[i] += yvel[i]

        sampler.sampleData(xpos, ypos)
Ich hab da verschiedenste Sachen getestet und nachgeprüft, die Simulation funktioniert jedenfalls, die Koordinaten werden korrekt aktualisiert.
Das Problem ist folgendes: wenn ich am Ende der Simulation die Liste (von X-Y-Listen) ausgebe, die die Koordinaten der Teilchen während der Simulation enthalten soll, so stehen an jeder Stelle die Koordinaten, die die Teilchen im allerletzten Schritt hatten. Auch die früheren Koordinaten werden durch diese letzten Koordinaten ersetzt/ überschrieben.

Das kann ich mir beim besten Willen nicht erklären, weil ich ja nur an die Liste anhänge und sonst nichts damit geschieht. Wieso werden dann die ursprünglichen Werte überschrieben??

Der komplette Code ist hier:
particles.py
http://paste.pocoo.org/show/207443/

sampler.py (genannt th3.py)
http://paste.pocoo.org/show/207444/
[url=http://www.proandkon.com]proandkon.com[/url]
BlackJack

@mzh: Du hängst da immer die *selben* Objekte an und in der Schleife in `particles` veränderst Du diese Objekte. Und wenn man ein Objekt verändert ist das natürlich überall sichtbar wo man darauf Zugriff hat. Vielleicht hilft das hier ja beim Verständnis:

Code: Alles auswählen

In [236]: xpos = range(5)

In [237]: xpos
Out[237]: [0, 1, 2, 3, 4]

In [238]: samples = list()

In [239]: samples.append(xpos)

In [240]: samples
Out[240]: [[0, 1, 2, 3, 4]]

In [241]: xpos[0] = 42

In [242]: samples
Out[242]: [[42, 1, 2, 3, 4]]

In [243]: samples.append(xpos)

In [244]: xpos[1] = 23

In [245]: samples
Out[245]: [[42, 23, 2, 3, 4], [42, 23, 2, 3, 4]]

In [246]: samples[0] is samples[1]
Out[246]: True
mzh
User
Beiträge: 295
Registriert: Dienstag 3. März 2009, 15:27
Wohnort: ZH

Danke für die Antwort.
Ich bin dein Beispiel durchgegangen und ich glaube ich habe es kapiert. Das hilft echt fürs Verständnis.
Nur ist mir jetzt ein bisschen schleierhaft, wie ich dass dann am besten beheben soll.
Ich hänge xpos an die Liste an und wenn xpos verändert wird, dann werden alle schon in der Liste befindlichen xpos-items mit verändert (auch in einem anderen Modul, hier sampler.py).
Ich müsste quasi die schon in der Liste befindlichen xpos-items versiegeln können.
[url=http://www.proandkon.com]proandkon.com[/url]
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

mzh hat geschrieben:Nur ist mir jetzt ein bisschen schleierhaft, wie ich dass dann am besten beheben soll.
Indem du deine Listen kopiert.

Code: Alles auswählen

    for s in range(nstep):
        xpos, ypos = xpos[:], ypos[:]
Wegen [:] siehe docs unter slicing.
mzh
User
Beiträge: 295
Registriert: Dienstag 3. März 2009, 15:27
Wohnort: ZH

Danke nochmals für den Hinweis.

Ich nehme an, die [:] Art zu kopieren ist copy.copy() vorzuziehen?
[url=http://www.proandkon.com]proandkon.com[/url]
BlackJack

@mzh: Du musst *neue* Listen anlegen und nicht immer die *selbe* Liste verändern. Und zwar würde ich das in der `simulateNSteps()` machen. In jedem Schritt neue Listen anlegen und da die aktualisierten Daten drin speichern, und auf die Daten aus dem letzten Schritt nur lesend zugreifen. Also grob:

Code: Alles auswählen

    for step in xrange(step_count):
        new_values = list()
        for i, value in enumerate(values):
            # 
            # Etwas mit dem Wert tun, aber das Objekt *nicht verändern*!
            # Sollte sich am Wert etwas ändern müssen, dann ein neues,
            # Objekt erstellen!
            # ...
            # 
            new_values.append(value)
        
        sampler.sample_data(new_values)
        values = new_values
Bei dem Quelltext fällt auf, dass zusammengehörende Daten in parallelen Listen verwaltet werden. Das ist kein guter Stil. Es macht den Quelltext schwerer verständlich und es kann leichter passieren, dass sich Fehler bei den ganzen Indexzugriffen einschleichen und die Listen nicht mehr synchron sind. IMHO würde es Sinn machen `Partikel`-Objekte einzuführen statt die Eigenschaften und das Verhalten von Partikeln über so viele unzusammenhängende Datenstrukturen und Funktionen zu verteilen.

Du solltest auch den ganzen Quelltext auf Modulebene in eine `main()`-Funktion stecken und Dich bei Konstanten an Namenskonventionen halten (NAME_IN_GROSSBUCHSTABEN), damit deutlicher wird, wo Werte herkommen. Einiges davon würde ich auch als Argumente übergeben, wie zum Beispiel die Anzahl der Simulationsschritte. Kann man ja trotzdem noch in einer Konstanten vorgeben, aber in die Funktionen sollte das als Argument reinkommen, so dass man auch mal mit kleineren Werten testen kann ohne die Konstante ändern zu müssen.

Für's Kopieren von Listen würde ich ``neu = list(alt)`` vorziehen. Dann kann `alt` im ersten Durchlauf, also das Argument was in die Funktion hereinkommt, ein beliebiges "iterable" sein. Das ist flexibler als die Slice-Notation.
mzh
User
Beiträge: 295
Registriert: Dienstag 3. März 2009, 15:27
Wohnort: ZH

Hey, merci für die Hinweise.
Ich schaue mir das ganze nochmals unter den Gesichtspunkten an, die du erwähnt hast.
[url=http://www.proandkon.com]proandkon.com[/url]
Antworten