List.remove löscht in mehren Listen gleichzeitig

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
XIII
User
Beiträge: 3
Registriert: Freitag 6. Dezember 2019, 09:01

Hallo, ich habe folgendes Problem:

Ich lösche das erste Element der Liste Zutaten. Allerdings wird dabei auch gleichzeitig genau dieses Element in der Liste GlobaleVariablen.Cocktails gelöscht, obwohl ich dies nirgendwo angewiesen habe. Kann mir jemand weiterhelfen? Ich war davon ausgegangen, wenn ich eine neue Liste Zutaten erstelle und in diese einen Teil der Liste Cocktails kopiere, dass ich dann mit der Liste Zutaten arbeiten kann ohne die Ursprungsliste Cocktails zu beeinflussen.

Code: Alles auswählen

def Zusammensetzungsliste(Getraenkename):
    #Liste nach Name durchsuchen
    Spalteninhalt = GlobaleVariablen.Cocktails[0]
    Zutaten = []
    for i in GlobaleVariablen.Cocktails:
        if Getraenkename in i:
            Zutaten = i

    Spalteninhalt.remove(Spalteninhalt[0])
    Zutaten.remove(Zutaten[0])
    for i in GlobaleVariablen.Getraenkeanschluesse:
        Name = "".join(i)
        if Name in Spalteninhalt:
            zsListe.append(Zutaten[Spalteninhalt.index(Name)])
    Ansteuerungsliste = [int(i) for i in zsListe]
Das obere Bild ist der Zustand vor Ausführung des Befehls, das untere danach. Jeweils mit Inhalt der Listen. Der Begriff "PinaColada" soll in diesem Beispiel entfernt werden

Bild
Jankie
User
Beiträge: 592
Registriert: Mittwoch 26. September 2018, 14:06

Du kannst eine Liste kopieren mit new_list = old_list.copy(), das andere ist nur ein Verweis auf die Liste, da hat hier jemand letztens was interessantens gepostet, ich such das mal.



#edit:

Gefunden: Facts and myths about Python names and values
Benutzeravatar
noisefloor
User
Beiträge: 4187
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,

was du machst ist nur eine 2. Referenz auf die Variable anlegen, damit wirken sich Änderungen auf _beide_ aus. Du musst halt eine Kopie und damit ein neues Objekt anlegen.

Beispiel:

Code: Alles auswählen

>>> a = [1, 2, 3]
>>> b = a
>>> b
[1, 2, 3]
>>> id(b), id(a)
(2075061871176, 2075061871176)
>>> b.pop()
3
>>> a
[1, 2]
>>> x = [1, 2, 3]
>>> y = x[:]
>>> y
[1, 2, 3]
>>> id(x), id(y)
(2075065485448, 2075062369352)
>>> y.pop()
3
>>> x
[1, 2, 3]
>>>
Weiterführende Infos zum Anlegen einer Kopie: Link.

Gruß, noisefloor
XIII
User
Beiträge: 3
Registriert: Freitag 6. Dezember 2019, 09:01

Vielen Dank
XIII
User
Beiträge: 3
Registriert: Freitag 6. Dezember 2019, 09:01

Jankie hat geschrieben: Freitag 6. Dezember 2019, 09:53 Du kannst eine Liste kopieren mit new_list = old_list.copy(), das andere ist nur ein Verweis auf die Liste, da hat hier jemand letztens was interessantens gepostet, ich such das mal.



#edit:

Gefunden: Facts and myths about Python names and values
new_list = old_list.copy() bringt genau das gleiche Ergebnis, remove entfernt jetzt in allen 3 Listen den Eintrag
Bei new_list = old_list[:] und new_list = list(old_list) ebenfalls

Bild
Sirius3
User
Beiträge: 18266
Registriert: Sonntag 21. Oktober 2012, 17:20

@XIII: globale Variablen werden nicht dadurch besser, dass man sie in ein klassenartiges Objekt `GlobaleVariablen` packt.
Generell sollte man keine Listen in irgendeiner Funktion verändern, wie Du ja selbst bemerkt hast. Funktionen sollten in sich abgeschlossene Einheiten sein, die alles was sie brauchen über ihre Argumente bekommen, und das Ergebnis per `return` zurückliefern, ohne sonstige Seiteneffekte, bedeutet, nichts an den übergebenen Argumenten verändern.

Nach Konvention werden Funktionsnamen und Variablennamen klein_mit_unterstrich geschrieben. Funktionen sollten nach Tätigkeiten benannt werden `Zusammensetzungsliste` ist aber keine.

` remove` ist ein ziemlich umständlicher Weg, um ein Element zu löschen, wenn man dessen Index schon kennt (`del Spalteninhalt[0]`), aber wie schon geschrieben, macht man das nicht. `i` ist ein schlechter Name für einen Cocktail und auch nicht für einen ` Getraenkeanschluss`.

`Zutaten` wird mit einer leeren Liste initialisiert, die aber dann zu einem IndexError führt, falls der Getränkenamen nicht existiert. In diesem Fall sollte eine aussagekräftigere Fehlermeldung erzeugt werden, und die Zuweisung wird überflüssig.

Wie sind denn die Datenstrukturen aufgebaut? Es ist komisch den Getränkenamen per `in` zu suchen, wo es sich doch offensichtlich um eine Zutatenliste handelt. Das `remove` läßt befürchten, dass der erste Eintrag der Liste der Name ist und der Rest die Zutaten. Das ist eine ganz schlechte Idee. Eigentlich sollte das dann ein Wörterbuch sein.
Der erste Eintrag der Coktails scheint auch eine besondere Bedeutung zu haben. Das ist wieder eine schlechte Idee. Wenn Du den Inhalt einer Tabelle liest, wo erste Zeile und Spalte besondere Bedeutungen haben, dann packt man das beim Einlesen in sinnvolle Strukturen und programmiert nicht überall, wo man darauf zugreift umständlichen Zugriffscode.
Was Du mit `Name = " ".join(i)` machst, verstehe ich ohne den Inhalt von Getraenkeanschluesse zu kennen, überhaupt nicht.
`zsliste` scheint irgendeine globale Liste zu sein. Die wird da gefüllt und beim nächsten Aufruf weiter gefüllt, so dass da noch alte Daten drin sind?

Ohne dass man an der Datenstruktur etwas ändert, sähe das so aus. Der Code an sich ist zwar in Ordnung, aber viel zu kompliziert und fragil, weil die Inputdaten nicht gut sind.

Code: Alles auswählen

def generiere_zusammensetzung(cocktails, getraenke_anschluesse, getraenkename):
    for cocktail in cocktails:
        #TODO: bessere Datenstruktur!
        if getraenkename in cocktail:
            zutaten = cocktail[1:]
            break
    else:
        raise KeyError(f"Cocktail {getraenkename} nicht gefunden")

    spalten_inhalt = cocktail[0][1:] # TODO: bessere Datenstruktur!
    ansteuerung = []
    for anschluss in getraenke_anschluesse:
        name = " ".join(anschluss) # Was soll das bedeuten?
        index = spalten_inhalt.index(name)
        if index >= 0:
            ansteuerung.append(int(zutaten[index]))
    return ansteuerung
PS: hast Du irgendwas mit viewtopic.php?f=6&t=47120 zu tun, denn dort werden auch Cocktails gemischt und der OP hat die selben Probleme mit globalen Zuständen, die er unter allen Umständen verändern will.


EDIT: anhand der Bilder sehe ich ja den Inhalt der Datenstrukturen, und sie sind wirklich schlecht.
Besser so:

Code: Alles auswählen

cocktails = {
    "PinaColada": {
        "Cola": 0,
        "Baccardi": 2,
        "Orangensaft": 5,
    },
    "Cuba Libre": {
        "Cola": 10,
        "Baccardi": 3,
        "Orangensaft": 0,
    }
}
Dann wird aus der Funktion:

Code: Alles auswählen

def generiere_zusammensetzung(cocktails, getraenkename):
    zutaten = cocktails[getraenkename]
    return zutaten

Die Struktur von Getraenkeanschluesse muß auch ein Wörterbuch sein, das Zutaten zu irgendwelchen Anschlussdaten mapt.
Antworten