Aufgabenverwaltung

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.
Xfd7887a
User
Beiträge: 135
Registriert: Montag 23. Juni 2014, 17:11

Hallo!
Ich bin relativ neu bei Python und hätte einige Fragen. Hier mein aktuelles Projekt: https://github.com/toxinman/stuff/blob/master/todo.py

Dazu meine Frage:
Wie kann ich ein Dictionary nach einem bestimmten Wert sortieren? Das dict besitzt die folgende Form:

Code: Alles auswählen

{"Fach": ["Datum", "Beschreibung", "Note", "Noch verbleibende Tage(int)"], "Englisch": ["2014-06-27", "LK", true, 4]}
Ich möchte nach den noch verbleibenden Tagen sortieren.
BlackJack

@Xfd7887a: Wörterbücher selbst kann man nicht sortieren, aber die `sort()`-Methode auf Listen kennt ein `key`-Argument welches Du Dir mal anschauen kannst. Genau wie die `sorted()`-Funktion die man auf beliebige iterierbare Objekte anwenden kann und die dann eine sortierte Liste aus den Elementen des iterierbaren Objekts erstellt.
Xfd7887a
User
Beiträge: 135
Registriert: Montag 23. Juni 2014, 17:11

Danke für den Tipp, BlackJack (cooler Name übrigens :D). Ich habe das gefunden:

Code: Alles auswählen

for key in sorted(aufgaben.iterkeys()):
        print "%s: %s" % (key, aufgaben[key][-1])
Wie kann ich jetzt nach dem letzten Element der Liste sortieren?
BlackJack

@Xfd7887a: Noch ein paar Anmerkungen zum Code:

Die `datum_auswerten()`-Funktion ist reichlich kompliziert und macht zu viel. Statt da mit `index()` und „slices” zu operieren hätte man viel leichter an Punkten splitten können. Oder man hätte auch die `strptime()`-Methode von `datetime`-Objekten verwenden können.

Das Berechnen des Countdown ist soweit ich das sehe kaputt. Das kommt vor allen daher das Du auf einer Zeichenkettendarstellung eines Datentyps Operationen ausführst, die man eigentlich direkt mit den Datentyp anstellen sollte. Dieses in Zeichenkette umwandeln, damit etwas machen, um das Ergebnis wieder in eine Zahl umzuwandeln, wenn man doch direkt die Zahl hätte abfragen können, ist ein hässlicher Hack. Und wie gesagt, kaputt. Bei Sachen die mehr als 9 Tage entfernt liegen klappt das nicht mehr. Und bei negativen Zahlen versuchst Du das '-' mit `int()` in eine Zahl zu wandeln, was nicht geht. Und wenn das Datum von heute ist, dann funktioniert das nur zufällig weil dann zwar eine 0 umgewandelt wird, die steht aber in der Zeichenkettendarstellung für Stunden und nicht für Tage.

Wenn man in einer Schleife über ein Wörterbuch sowohl die Schlüssel als auch die dazugehörigen Werte benötigt, dann kann man auch gleich über `items()` iterieren, beziehungsweise über `viewitems()` in Python 2.7, damit nicht tatsächlich erst eine Liste erstellt werden muss.

Dateien die man öffnet, sollte man auch wieder schliessen. Am besten öffnet man die Datei dazu im Zusammenhang mit der ``with``-Anweisung.

Der Countdown-Wert hat eigentlich nichts in der Datei zu suchen, denn der stimmt ja nur an dem Tag an dem der Eintrag erzeugt wurde. Wenn man die Datei an einem anderen Tag wieder öffnet, dann ist der Wert falsch.

Zum sortieren: Schau Dir das `key`-Argument von `sorted()` an. *Dort* musst Du eine *Funktion* übergeben die *ein* Element übergeben bekommt, und den Sortierschlüssel zurück gibt.
Xfd7887a
User
Beiträge: 135
Registriert: Montag 23. Juni 2014, 17:11

Danke für die Tipps :D ! Werde die jetzt Stück für Stück einarbeiten. Das Sortieren funktioniert jetzt auch.
Der Countdown-Wert hat eigentlich nichts in der Datei zu suchen, denn der stimmt ja nur an dem Tag an dem der Eintrag erzeugt wurde. Wenn man die Datei an einem anderen Tag wieder öffnet, dann ist der Wert falsch.
Gut, dass ich den Thread aufgemacht hat. Das hätte ich glatt übersehen. Wie könnte man den Countdown dann implementieren?
BlackJack

@Xfd7887a: Wozu brauchst Du den Countdown denn überhaupt im Datensatz? Zum sortieren schon mal nicht, denn da kommt immer die gleiche Reihenfolge bei heraus als wenn Du einfach nach dem Datum sortierst. Und wenn Du zu jedem Datensatz anzeigen möchtest wieviele Tage noch bis zum Datum sind, oder wieviele Tage er überfällig ist, kannst Du das auch zur Anzeige jeweils ausrechnen. Dazu wäre es praktisch wenn das Datum im Speicher nicht als Zeichenkette sondern als Datumsobjekt gehalten wird. Man sollte sich von der externen Darstellung der Daten in Dateien nicht diktieren lassen wie man die im Speicher vorhält, auch wenn beides recht ähnlich sein kann.
Sirius3
User
Beiträge: 18266
Registriert: Sonntag 21. Oktober 2012, 17:20

@Xfd7887a: ist es nicht etwas unsinnig, das Fach als Schlüssel zu benutzen. Oder gibt es pro Fach nur eine Aufgabe?
Xfd7887a
User
Beiträge: 135
Registriert: Montag 23. Juni 2014, 17:11

Danke für die Antworten. Ja, es gibt pro Fach nur eine Aufgabe.
Xfd7887a
User
Beiträge: 135
Registriert: Montag 23. Juni 2014, 17:11

Ich habe versucht einige Tips umzusetzen. Dabei ist das: http://github.com/toxinman/stuff/blob/master/todo.py herausgekommen. Ich weiß jedoch immer noch nicht wie ich nach der verbleibenden Zeit sortieren soll :|. Hat jemand noch ein Tipp für mich?
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Es wurden dir doch schon die sorted-Funktion und dessen key-Parameter genannt. Du musst jetzt nur noch in die Dokumentation schauen und dir den Abschnitt zu sorted durchlesen. Dort gibt es sogar Beispiele wie der key-Parameter zu verwenden ist!
Das Leben ist wie ein Tennisball.
BlackJack

@Xfd7887a: In `schnell_aufgabe_erstellen()` ist das `update()` ein wenig umständlich. Das ist nicht der offensichtliche Weg um einem Wörterbuch *ein* Schlüssel/Wert-Paar hinzuzufügen.

Gleiches gilt eigentlich auch für `neue_aufgabe()` und der Stelle an der der Rückgabewert verwendet wird.

Wie gesagt würde ich das Datum nicht in eine Zeichenkette umwandeln. Dann musst Du jedes mal wenn Du das verwendest wieder in ein `datetime.date`-Objekt umwandeln. Das ist umständlich und fehleranfällig.

`abkuerzung()` ist missverständlich benannt und der Docstring ist ebenfalls missverständlich bis falsch. Da werden nicht Fächer den Abkürzungen zugeordnet sondern umgekehrt. Das Wörterbuch ändert sich ja nicht, also könnte man das als Konstante ausserhalb der Funktion definieren. Die Abfrage geht mit der `dict.get()`-Methode auch viel einfacher.

Code: Alles auswählen

def abkuerzung_expandieren(fach):
    """Zuordnung der Abkuerzungen zu den Faecher"""
    return ABKUERZUNG_ZU_FACH.get(fach, fach)
Warum gibt `datum_auswerten()` kein Datum sondern Datum + Zeit zurück wobei die Zeit *immer* 0 ist?

Das zweite Format ist nicht das „Amerikanische” sondern eher das internationale. Dafür gibt es nämlich eine ISO-Norm.

Man sollte nicht den gleichen Namen in der selben Funktion für unterschiedliche Dinge verwenden. Statt auf die Einzelteile des Datums per Index zuzugreifen hätte man sie auch an unterschiedliche Namen binden können. Das hätte den zusätzlichen Vorteil das die Zeile zum erstellen des Datumsobjekts nur *einmal* in der Funktion stehen müsste.

Funktionen sollten nicht ein übergebenes Objekt verändern *und* es als Rückgabewert haben. Die Veränderung ist beim Aufrufer sichtbar, und der Aufrufer muss das Objekt ja schon haben, sonst hätte er es nicht übergeben können. Also macht es keinen Sinn ein Objekt was er schon hat, noch mal als Rückgabwert zu bekommen.

`ausgabe_der_aufgaben()` sollte die Daten nur ausgeben und nicht auch nebenbei Datensätze verändern. Damit rechnet keiner.

Ein nacktes ``except`` ist keine gute Idee. Das behandelt tatsächlich *alle* Ausnahmen, also auch wenn man sich im ``try``-Block bei einem Namen vertippt hat. Solche Fehler sind dann oft sehr schwer zu finden.

Man sollte keine Daten immer wieder im Quelltext wiederholen. So etwas wie der Dateiname der Aufgabendatei sollte man als Konstante definieren oder als Argument übergeben (oder beides). Dann muss man das nur an *einer* Stelle im Programm ändern.

Wenn die Datei nicht geladen werden kann, dann verhält sich das Programm fehlerhaft. Denn `json.dump()` gibt `None` zurück, und nicht etwa die Datenstruktur. Selbst dann wäre der Code fehlerhaft denn Du speicherst da eine Zeichenkette mit geschweiften Klammern als Inhalt und nicht etwa ein leeres Wörterbuch was man an der Stelle eigentlich machen müsste/sollte. Oder man bindet dort einfach `aufgaben` an ein leeres Wörterbuch und lässt das schreiben in eine Datei an der Stelle bleiben.

Das `exit()` einfach so existiert ist eine Besonderheit von CPython. Die Funktion muss man streng genommen aus `sys` importieren. Ich wäre auch vorsichtig mit der Verwendung wenn es nicht zwingend sein muss.

Das die Dateien nicht wieder geschlossen werden würde ich als Programmfehler ansehen. Man verlässt sich da auf Implementierungsdetails und Dateihandles sind eine limitierte Systemressource.
Xfd7887a
User
Beiträge: 135
Registriert: Montag 23. Juni 2014, 17:11

@BlackJack Danke für die ausführliche Antwort. Werde die nächsten Tage daran arbeiten.
Xfd7887a
User
Beiträge: 135
Registriert: Montag 23. Juni 2014, 17:11

Ich habe gerade gemerkt, dass meine aufgabe_entfernen() Funktion die gesamte Datei löscht. Ich weiß nicht, wie ich das lösen kann. Liegt es an der Art, wie ich meine Datei speichere?
BlackJack

@Xfd7887a: Das liegt daran *was* Du in der Datei speicherst. Du solltest da vielleicht die Aufgaben drin speichern und nicht `None`.
Xfd7887a
User
Beiträge: 135
Registriert: Montag 23. Juni 2014, 17:11

DANKE! :D
Xfd7887a
User
Beiträge: 135
Registriert: Montag 23. Juni 2014, 17:11

Arbeite gerade an weiteren Verbesserungen, nur leider verstehe ich nicht, was du mit
Das die Dateien nicht wieder geschlossen werden würde ich als Programmfehler ansehen. Man verlässt sich da auf Implementierungsdetails und Dateihandles sind eine limitierte Systemressource.
meinst. Wie soll ich die Datei schließen?
BlackJack

@Xfd7887a: Dateiobjekte haben eine `close()`-Methode dafür. Oder man verwendet das `open()` zusammen mit der ``with``-Anweisung.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Xfd7887a hat geschrieben:Wie soll ich die Datei schließen?
So:

Code: Alles auswählen

# Nutze die ``close``-methode auf File-Objekten:
file_object.close()
Besser, weil mit Absicherung gegen Ausnahmen während des Ablaufs, ist aber folgendes:

Code: Alles auswählen

with open(...) as file_object:
    # hantiere hier mit dem Objekt, rufe *nicht* ``close`` auf!
# das passiert nach Verlassen des Blocks automatisch :-)
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Xfd7887a
User
Beiträge: 135
Registriert: Montag 23. Juni 2014, 17:11

Ok, danke. Wenn ich nun aber versuche, die Datei zu schließen, kommt folgende Meldung:

Code: Alles auswählen

AttributeError: 'dict' object has no attribute 'close'.
Das ist sicherlich logisch, da ich den Inhalt der Datei unter der Variablen "AUFGABEN" gespeichert habe.

Wie komme ich jetzt an die eigentliche Datei ran?
BlackJack

@Xfd7887a: Du musst diese Methode auf dem *Dateiobjekt* aufrufen, und nicht auf dem Wörterbuch mit den Aufgaben. Dazu musst Du dieses Dateiobjekt an einen Namen binden. Besser wäre aber das mit der ``with``-Anweisung zu machen.
Antworten