Seite 2 von 3
Re: Adressbuch
Verfasst: Montag 22. September 2014, 17:08
von Xfd7887a
Habe folgenden Code bearbeitet:
Code: Alles auswählen
def suchen(adressen, gesucht):
"""Einzelne Adresse suchen"""
suche = []
for adresse in adressen:
if gesucht.lower() in [x.lower() for x in adresse.values()]:
suche.extend(adresse)
return suche
Leider kommt folgende Fehlermeldung:
Code: Alles auswählen
/usr/local/bin/python3.3m /Users/martin/Documents/Projekte/_stuff/Adressbuch_Python/Adressbuch.py
Traceback (most recent call last):
File "/Users/martin/Documents/Projekte/_stuff/Adressbuch_Python/Adressbuch.py", line 104, in <module>
main()
File "/Users/martin/Documents/Projekte/_stuff/Adressbuch_Python/Adressbuch.py", line 97, in main
adresse_anzeigen(adressen, suchen(adressen, "Kins"))
File "/Users/martin/Documents/Projekte/_stuff/Adressbuch_Python/Adressbuch.py", line 70, in adresse_anzeigen
adresse = suchen(adressen, name)
File "/Users/martin/Documents/Projekte/_stuff/Adressbuch_Python/Adressbuch.py", line 56, in suchen
if gesucht.lower() in [x.lower() for x in adresse.values()]:
AttributeError: 'list' object has no attribute 'lower'
Re: Adressbuch
Verfasst: Montag 22. September 2014, 17:22
von BlackJack
@Xfd7887a: Nun ganz offensichtlich ist entweder `gesucht` oder mindestens ein Wert für `x` eine Liste. Finde heraus welches und warum.
Re: Adressbuch
Verfasst: Montag 22. September 2014, 18:17
von Xfd7887a
Ok, danke. Mein bisheriger Stand:
https://github.com/toxinman/stuff/blob/ ... essbuch.py Morgen mache ich weiter.
Re: Adressbuch
Verfasst: Dienstag 23. September 2014, 08:23
von Hyperion
Wieso wird denn beim Anzeigen immer noch die ``suchen``-Funktion aufgerufen? Was soll die da bewirken? Ich schrieb das schon einmal: Eine Ausgabe setzt doch implizit voraus, dass man etwas hat, was man ausgeben will. Wieso muss ich das dann erst suchen

Das solltest Du dringend ändern!
(Abgesehen davon, dass das Ausgeben von einer Adresse ohne Not aktuell eine Zeitkomplexität von O(n) aufweist und das Ausgeben von allen Addressen damit sogar O(n^2)!)
Re: Adressbuch
Verfasst: Mittwoch 24. September 2014, 14:17
von _nohtyp_
Wieso wird denn beim Anzeigen immer noch die ``suchen``-Funktion aufgerufen? Was soll die da bewirken? Ich schrieb das schon einmal: Eine Ausgabe setzt doch implizit voraus, dass man etwas hat, was man ausgeben will. Wieso muss ich das dann erst suchen

Das solltest Du dringend ändern!
Ich weiß leider nicht, wie ich das anders machen könnte. Wenn ich die Suche entferne, also schon in der main-Funktion suchen lasse, passt das nicht mehr mit der "adressen_ausgeben"-Funktion.
Re: Adressbuch
Verfasst: Mittwoch 24. September 2014, 14:38
von BlackJack
@_nohtyp_: Du musst die `adresse_ausgen()` so ändern das man der *eine* Adresse übergibt, die dann ausgegeben wird. Also das naheliegendste und einfachste. Und beim Aufruf übergibt man der Funktion dann *eine* Adresse.
Re: Adressbuch
Verfasst: Mittwoch 24. September 2014, 14:48
von _nohtyp_
Ok, ich habe es geändert
Re: Adressbuch
Verfasst: Mittwoch 24. September 2014, 14:48
von Hyperion
_nohtyp_ hat geschrieben:
Ich weiß leider nicht, wie ich das anders machen könnte. Wenn ich die Suche entferne, also schon in der main-Funktion suchen lasse, passt das nicht mehr mit der "adressen_ausgeben"-Funktion.
Na übergibst eben *ein* fertiges Adressen-Objekt :K Wo ist da denn das Problem? Imho geht das doch kaum einfacher...
Re: Adressbuch
Verfasst: Mittwoch 24. September 2014, 14:51
von Hyperion
_nohtyp_ hat geschrieben:Ok, ich habe es geändert
OMG... doch nicht so:
Code: Alles auswählen
def adressen_anzeigen(adressen):
"""Alle Adressen werden formatiert ausgegeben"""
for adresse in adressen:
adresse_anzeigen(adressen, suchen(adressen, adresse["Name"]))
print()
def adresse_anzeigen(adressen, adresse):
"""Eine Adresse wird formatiert ausgegeben"""
for key in adresse:
print("{}: {}".format(key, adresse[key]))
Schau Dir doch mal an, *was* Du in der Funktion ``adresse_anzeigen`` wirklich von den übergebenen Parametern nutzt! Und dann überlege, inwiefern der Aufruf in der ``adressen_anzeigen``-Funktion sinnvoll ist
Du machst es Dir irgend wie schrecklich kompliziert...
Re: Adressbuch
Verfasst: Mittwoch 24. September 2014, 14:55
von _nohtyp_
Sorry, aber ich blicke nicht durch, was ich ändern muss. Das bei "adresse_anzeigen" "adressen" eigentlich nicht gebraucht wird, sehe ich. Aber wenn ich "adressen_anzeigen" aufrufe, brauche ich es.
Re: Adressbuch
Verfasst: Mittwoch 24. September 2014, 15:06
von Hyperion
_nohtyp_ hat geschrieben:Sorry, aber ich blicke nicht durch, was ich ändern muss. Das bei "adresse_anzeigen" "adressen" eigentlich nicht gebraucht wird, sehe ich. Aber wenn ich "adressen_anzeigen" aufrufe, brauche ich es.
Das eine hat doch mit dem anderen nichts zu tun
Code: Alles auswählen
def adressen_anzeigen(adressen):
"""Alle Adressen werden formatiert ausgegeben"""
for adresse in adressen:
adresse_anzeigen(adresse)
print()
def adresse_anzeigen(adresse):
"""Eine Adresse wird formatiert ausgegeben"""
for key, value in adresse.items():
print("{}: {}".format(key, value))
Einfach, oder?
Das ist beim Programmieren übrigens ein durchaus häufiges Schema: Man erweitert Funktionalität, indem man auf *bestehendes* aufbaut. Du hattest zuerst ja eine Funktion, die *eine* Adresse angezeigt hat. Die *logische* (und von mir damals vorgeschlagene) Erweiterung dient dann dazu, eine *Sammlung* von Adressen anzuzeigen. Dazu muss man dann ja nur durch die Sammlung iterieren und für *jedes einzelne* Element die bestehende Funktion aufrufen.
Also nach diesem Schema:
Code: Alles auswählen
def tue_etwas_fuer_ein_objekt(ein_objekt):
pass
def tue_etwas_fuer_viele_objekte(objekte):
for ein_objekt in objekte:
tue_etwas_fuer_ein_objekt(ein_objekt)
Du hast stattdessen die Schleife *in* die bestehende Funktion eingebaut und damit nahm das "Übel" seinen Lauf

Re: Adressbuch
Verfasst: Mittwoch 24. September 2014, 15:21
von _nohtyp_
Genial, danke.
Einfach, oder?
Ja, eigentlich schon. Bin aber nicht drauf gekommen, ist manchmal halt so.

Re: Adressbuch
Verfasst: Mittwoch 24. September 2014, 15:25
von Hyperion
Versuche Dir, das "Schema" zu merken und Dich bei Zeiten daran zu erinnern

Re: Adressbuch
Verfasst: Mittwoch 24. September 2014, 15:28
von _nohtyp_
Werde ich machen. Zu den Indexen: Sollte ich den Index gleich mit in das Dictionary einer Adresse setzen, oder einzeln. Also entweder:
oder
?
Re: Adressbuch
Verfasst: Mittwoch 24. September 2014, 16:05
von /me
_nohtyp_ hat geschrieben:
Ähhh?
Code: Alles auswählen
>>> {{"Name": "Python"}: "ID"}
Traceback (most recent call last):
File "<input>", line 1, in <module>
TypeError: unhashable type: 'dict'
Re: Adressbuch
Verfasst: Mittwoch 24. September 2014, 17:40
von _nohtyp_
Ok, also nehme ich die erste Variante. Danke

Re: Adressbuch
Verfasst: Freitag 26. September 2014, 20:05
von _nohtyp_
Habe ein wenig weitergearbeitet:
https://github.com/toxinman/stuff/blob/ ... essbuch.py Nicht hauen, vieles ist noch Mist

Re: Adressbuch
Verfasst: Freitag 26. September 2014, 20:36
von EyDu
Hallo,
ein paar Dinge kannst du da noch verbessern, ich fange einfach mal oben an:
Zum Erzeugen der Dateinamen könntest du dir eine kleine Funktion schreiben. Sowohl beim Laden als auch beim Speichern hängst du am Ende ein ".json" an den Namen dran. Hast du das an einer Stelle, dann bist du etwas flexibler.
String solltest du nicht mittels + zusammensetzen. Das ist nicht nur langsam, was in deinem Fall keine Rolle spielt, es sieht auch noch unübersichtlich aus. Benutze einfach String Formatting, dann sieht das Ende von Zeile 27 auch sauber aus.
Eine Zeile sollte nicht länger als 80 Zeichen sein. Das fällt besonders in Zeile 27 auf. Schon wegen der Übersichtlichkeit würde ich jedem Eintag eine eigene Zeile spendieren, dann liest es sich leichter.
Der enumerate-Funktion in Zeile 38 kannst du einen Startindex mitgeben, dann musst du in der Folgezeile nicht bei jedem Durchlauf eins drauf addieren.
In deinem try-Block sollte nur das nötigste stehen. Auf keinen Fall mehr. Zeilen 42 und 43 sind dafür ein Beispiel, du hast aber noch mehr davon. Der try-Block enthält die Eingabe, die Umwandlung in einen Integer und die Abfrage. Du prüfst aber nur die Fehler bei der Umwandlung in einen Integer. Ziehe also die Eingabe davor und packe lediglich den int-Aufruf (inklusive einer Zuweisung) in den try-Block. Den Zugriff auf "suche" kannst du in einem else-Block machen, welches du einem try/except-Konstrukt hinzufügen kannst. Dann ist auch sichergestellt, dass du nicht aus Versehen Ausnahmen behandelst, welche du nicht behandeln willst/kannst. Zum Beispiel in anderen Funktionen.
Für Benutzereingaben solltest du dir eine Funktion schreiben, dann musst du dich nicht ständig wiederholen.
Zeile 45 ist keine gute Idee, rekursive Aufrufe solltest du in Python vermeiden. Zumindest solche, welche beliebig tief werden können. Das könnte irgendwann zu einem Problem werden. Verwende besser eine while-Schleife. Üblicherweise verwendet man eine ``while True:``-Schleife und verlässt diese im Erfolgsfall mittels break. Das selbe Konstrukt hast du an einer anderen Stelle noch einmal.
Du solltest für die Schlüssel in deinem Dictionary Konstanten verwenden. Sonst fliegen überall in deinem Programm magische Strings durch die Gegend, deren Bedeutung unklar ist. Ganz problematisch wird es, wenn du einen solchen String mal ändern willst/musst. Dann musst du nämlich alle Vorkommen im Programm finden. Das funktioniert aber meistens nicht auf Anhieb. Hast du hingegen Konstanten, musst du das nur an genau einer Stelle erledigen.
Telefonnummern sind keine Integer.
Re: Adressbuch
Verfasst: Freitag 26. September 2014, 21:02
von _nohtyp_
@EyDu Erstmal Danke für deine Antwort

Ich habe ein paar Fragen dazu:
Zum Erzeugen der Dateinamen könntest du dir eine kleine Funktion schreiben. Sowohl beim Laden als auch beim Speichern hängst du am Ende ein ".json" an den Namen dran. Hast du das an einer Stelle, dann bist du etwas flexibler.
Meinst du damit, dass ich die Dateiendung als Konstante definieren soll?
Für Benutzereingaben solltest du dir eine Funktion schreiben, dann musst du dich nicht ständig wiederholen.
Wie meinst du das? Unterscheiden sich die Benutzereingabe dafür nicht zu sehr?
Üblicherweise verwendet man eine ``while True:``-Schleife und verlässt diese im Erfolgsfall mittels break.
In dieser Form:
Code: Alles auswählen
while True:
auswahl = input("Welche Adresse haben Sie gesucht? >> ")
try:
auswahl = int(auswahl)
except (ValueError, IndexError) as e:
print(e)
else:
break
Oder geht das auch kürzer?
Du solltest für die Schlüssel in deinem Dictionary Konstanten verwenden.
Und dann den entsprechenden Schlüssel durch z.B.
ersetzen?
Das war erstmal alles

Re: Adressbuch
Verfasst: Samstag 27. September 2014, 09:23
von Hyperion
_nohtyp_ hat geschrieben:
Du solltest für die Schlüssel in deinem Dictionary Konstanten verwenden.
Und dann den entsprechenden Schlüssel durch z.B.
ersetzen?
Nee, um Gottes Willen nein. Sondern z.B. so:
Code: Alles auswählen
NAME = "name"
VORNAME = "vorname"
...
# und zur Verwendung
adresse[NAME]
Der Vorteil davon, den EyDu meinte, kannst Du schnell begreifen, wenn Du aus ``name`` nun lieber ``nachname`` machen willst. Jetzt müsstest Du nur die Definition Deiner Konstanten ändern, der Rest vom Code bliebe gleich.
Ich selber wäre mir aber nicht sicher, ob ich das wirklich machen wollen würde. Ist hier vielleicht ein wenig Geschmackssache. Imho ist der Name eines Schlüssels in dieser Problemdomäne vergleichbar mit einem Attributsnamen einer Klasse. Wenn ich den ändern will, muss ich normaler Weise gute Gründe haben und eh refactoring betreiben
Ich finde aber andere Dinge eher verbesserungsbedürftig. Du verfällst schon wieder in das Muster, *zu viel* und vor allem *unterschiedliche* Dinge in *eine* Funktion zu packen!
Nehmen wir ``suchen`` mal als Beispiel, ich habe meine Kritik als Kommentare hinein geschrieben:
Code: Alles auswählen
def suchen(adressen, gesucht):
"""Adresse wird gesucht. Nutzer wählt aus Ergebnissen Suche Eintrag."""
# ich würde ``suche`` eher ``such_ergbenisse`` oder nur ``ergebnisse`` oder
# ``gefunden`` nennen.
suche = []
for adresse in adressen:
if gesucht.lower() in [x.lower() for x in adresse.values()]:
suche.append(adresse)
# hier müsste eigentlich Schluss sein! Gib das Ergebnis zurück und gut ist!
# wieso nutzt Du nicht die Funktion für die Ausgabe von *mehreren* Adressen?
# nichts anderes ist doch in ``suche`` enthalten.
# eine wichtige Fähigkeit beim Programmieren ist es, wiederkehrende Aufgaben
# zu kapseln und darauf zurückzugreifen.
for idx, ergebnis in enumerate(suche, 1):
print("{}: {}".format(idx, ergebnis))
# Benutzereingaben / Entscheidungen gehören nicht in eine Funktion,
# die schlicht "suchen" soll. Du kannst hier gar nicht wissen, ob
# evtl. auch etwas mit *allen* Resultaten gemacht werden soll!
auswahl = input("Welche Adresse haben Sie gesucht? >> ")
try:
auswahl = int(auswahl)
except (ValueError, IndexError) as e:
print(e)
suchen(adressen, gesucht)
else:
return suche[auswahl - 1]
Ähnliches gilt für das Löschen oder auch das Ändern. Schreibe erst einmal nur Funktionen, die wirklich diese Dinge *tun* - ohne Nachfrage und ohne weitere Eingaben. So etwas regelst Du außerhalb und vor dem Aufruf einer Funktion.
Denk daran, dass Du ursprünglich eine GUI für dein Adressbuch haben wolltest. "Dank" der Mischung von Eingaben und dem Erledigen von Aufgaben in *einer* Funktion, kannst Du diese für eine GUI nicht mehr verwenden und müsstest das komplett neu schreiben. Den Prompt eines ``input`` siehst Du in einer GUI ja gar nicht! Du musst also schon bei der Benutzung über eine Textshell darauf achten, dass alle Eingaben und Ausgaben getrennt von der Kernfunktionalität stattfinden! Denn letztlich ist das auch schon eine Form von GUI - nur ohne das "G"
Vielleicht noch als Hinweis für die Gestaltung von solchen Textmenüs:
Tutorial