Lösung zu Seiteneffekt

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
schahramsigi
User
Beiträge: 13
Registriert: Sonntag 7. Juni 2020, 20:18

Hallo Forum
Ich habe mal eine Frage.

Code: Alles auswählen

a = [[a , b], [c, d]
b = [e, f]
a.append(b)
b.clear()
dann verschwendet b aus liste a!
Mit deepcopy schaffe ich das.
Meine Frage ist: Gibt es bessere Methoden?
Sirius3
User
Beiträge: 17747
Registriert: Sonntag 21. Oktober 2012, 17:20

Ja, erst gar nicht damit anfangen, Listen zu leeren.
xXSkyWalkerXx1
User
Beiträge: 379
Registriert: Mittwoch 27. Juni 2018, 17:39

Ich denke, wenn du etwas aus einer Liste entfernen möchtest, solltest du lieber "del" verwenden.

z. B.:

Code: Alles auswählen

a = [[a , b], [c, d]] #hier fehlte übrigens eine " ] "
b = [e, f]
a.append(b)
del a[2] # b ist 3. Element, aber man zählt ab 0, also 2
und hier etwas anschaulicher...

Code: Alles auswählen

a = [ [1,1], [2,2] ]
b = [3,3]
a.append(b)

print(a)
# Ausgabe: [[1, 1], [2, 2], [3, 3]]

print(a[2])
# Ausgabe: [3, 3]
[EDIT] Möchtest du komplett die Liste leeren, funktioniert das so: " del b[:] ", dadurch löscht du alle Inhalte der Liste.
Alternativ kannst die Liste auch komplett löschen: "del b"
Zuletzt geändert von xXSkyWalkerXx1 am Samstag 4. Juli 2020, 14:18, insgesamt 1-mal geändert.
Benutzeravatar
sparrow
User
Beiträge: 4193
Registriert: Freitag 17. April 2009, 10:28

@schahramsigi: Das problem an nicht funktionierenden Beispielen ist, dass sie nicht funktionieren. Syntaxfehler in Zeile 1 und die Elemente in Zeile 1 sind undefiniert. Und selbst wenn nicht, wäre es mehr als merkwürdid, eine Liste genauso zu bennen wie die Liste. Alsso a in eine Liste zu packen und die an den Namen a zu binden.
Benutzeravatar
sparrow
User
Beiträge: 4193
Registriert: Freitag 17. April 2009, 10:28

@xXSkyWalkerXx1: Ich glaube nicht, dass damit das "Problem" von schahramsigi gelöst wird.

Aber ich verstehe ehrlich gesagt auch das Problem nicht.

Code: Alles auswählen

>>> a = [["a", "b"], ["c", "d"]]
>>> b = ["e", "f"]
>>> a.append(b)
>>> print(a)
[['a', 'b'], ['c', 'd'], ['e', 'f']]
>>> id(b)
1415432304008
>>> id(a[2])
1415432304008
>>> b.clear()
>>> print(b)
[]
>>> print(a)
[['a', 'b'], ['c', 'd'], []]
>>> 
Für mich verhält sich das wie erwartet.
Da gibt es eine Liste, die ist sowohl an den Namen "b" gebunden als auch das 3. Element in der Liste, die an "a" gebunden ist. Aber es ist die selbe Liste.
schahramsigi
User
Beiträge: 13
Registriert: Sonntag 7. Juni 2020, 20:18

Danke
Ich weiß dass Python sich normal verhält.
Aber als Anfänger möchte ich nur wissen welche Methoden es gibt, wenn man das machen müsste.
Danke für den Tipp mit del[:]
Sirius3
User
Beiträge: 17747
Registriert: Sonntag 21. Oktober 2012, 17:20

@schahramsigi: wie schon geschrieben, man vermeidet es, dass Listen verändert werden, die wo anders benutzt werden, am einfachsten dadurch, dass man gar keine Listen verändert.
Für jeden Anwendungsfall gibt es so eine Lösung. Also was ist Dein wirkliches Problem?
schahramsigi
User
Beiträge: 13
Registriert: Sonntag 7. Juni 2020, 20:18

Darf ich das so posten? Wie gesagt Anfänger. Bestimmt habe ich 1000 Fehler!

Code: Alles auswählen

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from sys import exit
def main():
    with open("freunde.csv") as fr, open("freunde.csv", "a") as fw:
        liste = []
        gefunden = []
        neu = []
        for line in fr:
            line = line.strip().split(",")
            liste.append(line)
        while True:
            eingabe = input("Eingabe: (q=quit n=neu) ").lower()
            if eingabe == "q":
                break
            elif eingabe == "n":
                neu.append(input("Vorname? ").lower().strip())
                neu.append(input("Nachname? ").lower().strip())
                if neu[0] != "" and neu[1] != "":
                    if neu not in liste:
                        liste.append(neu)
                        fw.write("{},{}\n".format(neu[0], neu[1]))
                    else:
                        print("Exsistiert schon!")
                else:
                    print("Da fählt was!")
                del neu[:]
            for vorname, nachname in liste:
                if vorname == eingabe or nachname == eingabe:
                    gefunden.append(vorname)
                    gefunden.append(nachname)
            if gefunden != []:
                for x in range(0, len(gefunden)-1, 2):
                    print(gefunden[x].title(), gefunden[x+1].title())
            del gefunden[:]

if __name__ == "__main__":
    main()
exit("Aufwiedersehen")

Die Listen neu und gefunden muss ich immer löschen!
Benutzeravatar
__blackjack__
User
Beiträge: 13100
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@schahramsigi: Wieso musst Du die löschen/leeren? Musst Du gar nicht. Man würde ganz einfach eine neue leere Liste verwenden. Und zwar würde man die *vor* dem verwenden erstellen und nicht *nach* dem verwenden leeren. Das ist doch total undurchsichtig/unübersichtlich das ”falsch” herum zu machen.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Sirius3
User
Beiträge: 17747
Registriert: Sonntag 21. Oktober 2012, 17:20

@schahramsigi: sys.exit wird fast nie gebraucht, hier ist es auch unnötig und sollte als Argument eh eine Zahl (!= 0) haben.
Benutze keine Abkürzungen, statt `fr` `freunde`.
Eine Textdatei gleichzeitig zum Lesen und zum Schreiben zu öffnen, ist meist falsch, und hier sollte das auch auf zwei Teile aufgeteilt werden. Beim Öffnen sollte man immer ein encoding angeben
Zum Lesen von csv-Dateien gibt es das csv-Modul.
Statt nachträglich die Listen zu leeren, würde man dann, wenn man sie braucht, eine neue Liste erzeugen. `liste`, `neu` und `gefunden` sind schlechte Variablennamen, weil zu allgemein.
Bei Eingabe von "n" sollte doch nicht gesucht werden, da fehlt also ein `else`.
Wenn man eine feste Anzahl an Elementen einer Liste erzeugt, dann mit eckigen Klammern und nicht per append.
Magische Indizes sollte man nicht verwenden.
Beim Suchen ist es komisch, dass in der Liste Vor- und Nachname hintereinander in der Liste stehen.
Das macht die Ausgabe auch unnötig kompliziert.
Über einen Index zu iterieren ist dann auch nicht nötig.

Code: Alles auswählen

#!/usr/bin/env python3
import csv

def main():
    with open("freunde.csv", newline="", encoding="utf8") as lines:
        freunde = list(csv.reader(lines))

    with open("freunde.csv", mode="a", newline="", encoding="utf8") as output:
        output = csv.writer(output)
        while True:
            eingabe = input("Eingabe: (q=quit n=neu) ").lower()
            if eingabe == "q":
                break
            elif eingabe == "n":
                vorname = input("Vorname? ").lower().strip(),
                nachname = input("Nachname? ").lower().strip(),
                neuer_freund = [vorname, nachname]
                if vorname and nachname:
                    if neuer_freund not in freunde:
                        freunde.append(neuer_freund)
                        output.writerow(neuer_freund)
                    else:
                        print("Exsistiert schon!")
                else:
                    print("Da fehlt was!")
            else:
                gefundene_freunde = []
                for vorname, nachname in freunde:
                    if vorname == eingabe or nachname == eingabe:
                        gefundene_freunde.append([vorname, nachname])
                if gefundene_freunde:
                    for vorname, nachname in gefundene_freunde:
                        print(vorname.title(), nachname.title())

if __name__ == "__main__":
    main()
Statt alles mit Kleinbuchstaben zu speichern und beim Ausgeben zu Raten, was denn Groß geschrieben werden muß, solltest Du so wie eingegeben speichern und nur beim Suchen alles in Kleinbuchstaben umwandeln.
Benutzeravatar
__blackjack__
User
Beiträge: 13100
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@schahramsigi: Für das ``del name[:]`` solltest Du Dich nicht so wirklich bedanken, denn das ist ein Sonderfall und schwerer lesbar/recherchierbar als die `clear()`-Methode, die es auch auf Container-Objekten gibt bei denen Slicing generell nicht funktioniert, oder die das Löschen eines Slice nicht implementieren.

`fr` und `fw` sind keine guten Namen. Namen sollten dem Leser vermitteln was der Wert dahinter bedeutet und nicht zum Rätselraten zwingen. Der Name `liste` ist zu generisch. `neu` sagt auch nichts über den Inhalt.

Es macht auch nicht so viel Sinn beide Dateien in einen ``with``-Block zu stecken wenn die *deutlich* unterschiedliche Lebensdauern haben.

Textdateien sollte man immer explizit mit einer Kodierung öffnen.

Beim einlesen der Datei wird `line` erst an eine Zeichenkette gebunden und dann an eine Liste mit zwei Elementen. Das ist verwirrend. Hier würde sich eine „list comprehension“ auch nahezu aufdrängen.

Wie schon gesagt muss man `neu` und `gefunden` gar nicht leeren wenn man die nicht ganz weit am Anfang einmal leer erstellt, sondern immer dort wo man sie dann benötigt erst erstellt und füllt. Wobei man `neu` nicht leer erstellen muss, denn wenn danach sowieso gleich immer zwei `append()`-Aufrufe folgen, kann man an der Stelle auch gleich eine Liste mit diesen beiden Elementen erstellen.

Es macht nur unnötige Arbeit das in `gefunden` jedes zweite Element etwas anderes bedeutet. Da würde man einfach das Format von `liste` beibehalten und zusammengehörige Werte auch als *ein* Objekt behandeln. Das wäre dann auch wieder eine simple „list comprehension“. Und den Test ob etwas gefunden wurde braucht man nicht. Wenn nichts gefunden wurde, dann läuft der Körper der ``for``-Schleife ja auch ohne den unnötigen vorherigen Test nicht.

`sys.exit()` wird falsch verwendet. Wenn man da einen Text übergibt, dann ist der Rückgabecode 1, was für das aufrufende Programm so viel wie „es gab Probleme“ heisst, was ja aber gar nicht stimmt. Diese Funktion sollte man nur verwenden wenn man tatsächlich explizit einen anderen Rückgabecode als 0 („alles ok“) an den Aufrufer liefern will. Hier willst Du einfach nur ein `print()` und das auch *in* der Hauptfunktion und nicht auf Modulebene.

Ungetestet:

Code: Alles auswählen

#!/usr/bin/env python3


def main():
    with open("freunde.csv", encoding="utf-8") as lines:
        rows = [line.strip().split(",") for line in lines]

    with open("freunde.csv", "a", encoding="utf-8") as file:
        while True:
            eingabe = input("Eingabe: (q=quit n=neu) ").lower()
            if eingabe == "q":
                break

            if eingabe == "n":
                row = [
                    input(f"{what}? ").lower().strip()
                    for what in ["Vorname", "Nachname"]
                ]
                if "" in row:
                    if row not in rows:
                        rows.append(row)
                        file.write(",".join(row) + "\n")
                    else:
                        print("Exsistiert schon!")
                else:
                    print("Da fehlt was!")
            #
            # TODO Soll das eventuell in ein ``else`` damit nicht nach "n"
            #   gesucht wird?
            #
            gefunden = [row for row in rows if eingabe in row]
            for row in gefunden:
                print(" ".join(value.title() for value in row))

    print("Aufwiedersehen")


if __name__ == "__main__":
    main()
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
schahramsigi
User
Beiträge: 13
Registriert: Sonntag 7. Juni 2020, 20:18

Danke für eure ausführlichen Hilfe.
Ich habe noch viel zu lernen. :-)
schahramsigi
User
Beiträge: 13
Registriert: Sonntag 7. Juni 2020, 20:18

@Siris3
Was bedeutet Magische Indizes?
Sirius3
User
Beiträge: 17747
Registriert: Sonntag 21. Oktober 2012, 17:20

Das bedeutet, dass nicht sofort ersichtlich ist was die 0 bei neu[0] bedeutet oder die 1 für Nachname steht.
schahramsigi
User
Beiträge: 13
Registriert: Sonntag 7. Juni 2020, 20:18

Aha ok verstanden. Danke
Antworten