Aufgabenverwaltung
Code: Alles auswählen
Zum einen sind pickle-Dateien nicht menschenlesbar

# Edit: Apropos Klassen: Würde es Sinn machen, mein Programm mit Klassen auszudrücken? Wenn ich irgendwann ne GUI einprogrammiere, werde ich sicherlich nicht drum 'rum kommen.
@mutetella: Ergänzend zu mutetella's Anmerkungen: `pickle` ist auf Python beschränkt. (Falls es doch eine Bibliothek für eine andere Programmiersprache geben sollte: JSON-Bibliotheken gibt es für *sehr* viele Programmiersprachen.) Man könnte zum Beispiel zur Anzeige der Aufgaben eine Webseite mit JavaScript schreiben, die diese Datei nachlädt und dann anzeigt. Die HTML-Datei braucht man dann nur einmal auf den Webserver hochladen und zum aktualisieren bräuchte man nur noch die JSON-Datei erneut hochladen.
Bei diesem Programm ist das mit dem ”menschenlesbar” vielleicht noch nicht so interessant, aber es kann manchmal zur Fehlersuche hilfreich sein wenn man sich das gespeicherte Ergebnis ausserhalb der eigenen Anwendung anschauen kann.
Ich nutze `pickle` nur noch für kurzfristig gespeicherte Daten, zum Beispiel für Caches, oder zum serialisieren von Daten für die Übertragung zu anderen Python-Programmen. Oft auch indirekt weil entsprechende Bibliotheken das halt nutzen.
Bei diesem Programm ist das mit dem ”menschenlesbar” vielleicht noch nicht so interessant, aber es kann manchmal zur Fehlersuche hilfreich sein wenn man sich das gespeicherte Ergebnis ausserhalb der eigenen Anwendung anschauen kann.
Ich nutze `pickle` nur noch für kurzfristig gespeicherte Daten, zum Beispiel für Caches, oder zum serialisieren von Daten für die Übertragung zu anderen Python-Programmen. Oft auch indirekt weil entsprechende Bibliotheken das halt nutzen.
Das heißt, dass Du zwischen Speichern und Laden von Klassenexemplaren nichts an einer Klasse ändern darfst. Wenn Du also aus irgendeinem Grund der Klasse noch zusätzliche Attribute hinzufügst oder bestehende wegnimmst, kannst Du mit den bereits gepickelten Daten erstmal nichts mehr anfangen, da diese in die geänderte Klassenstruktur nicht mehr hineinpassen, da sie ja von den geänderten Attributen nichts wissen. In einem solchen Fall muss man also die alten Daten erstmal in die alte Klassenstruktur laden, diese Exemplare dann in die neue Klassenstruktur konvertieren und dann erst wieder abspeichern. Sehr mühsam und eigentlich unnötig, wenn man auf pickle im Produktiveinsatz verzichtet, da es ja eben ausreichend Ersatz gibt, z. B. eben json oder gleich 'ne Datenbanklösung, wenn das denn nötig ist.Xfd7887a hat geschrieben:Den anderen Punkt verstehe ich nicht, ich fange erst mit Klassen an.
mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit
)

Ich finde ja. Momentan befinden sich Deine Daten `aufgaben` auf Modulebene, wo Sie eigentlich nicht hingehören. Wenn Du dafür eine Klasse `Tasks` (ich bevorzuge ja englische Namen) erstellst fände ich das übersichtlicher. Zudem wäre dann zusammen, was zusammengehört: Die Attribute einer Aufgabe wie Fach, Datum etc. und die Methoden (Funktionen), die Du darauf anwendest. Zum Beispiel:Xfd7887a hat geschrieben:Apropos Klassen: Würde es Sinn machen, mein Programm mit Klassen auszudrücken?
Code: Alles auswählen
class Task(object):
def __init__(self, subject, date, theme, grade):
self.subject = subject
self.date = date
self.theme = theme
self.grade = grade
def __str__(self):
return 'Task: {s} {d} {t} {g}'.format(
s=self.subject, d=self.date,
t=self.theme, g=self.grade)
class Tasks(object):
def __init__(self):
self.tasks = set()
def add_task(self, task):
self.tasks.add(task)
def __iter__(self):
for task in self.tasks:
yield task
Code: Alles auswählen
>>> import todo
>>> tasks = todo.Tasks()
>>> tasks.add_task(todo.Task('Mathe', '28.06.2014', 'Bruchrechnen', 'befriedigend'))
>>> tasks.add_task(todo.Task('Deutsch', '27.06.2014', 'Rechdschraibuhnk', 'ungenuegend'))
>>> for task in tasks:
... print task
...
Task: Mathe 28.06.2014 Bruchrechnen befriedigend
Task: Deutsch 27.06.2014 Rechdschraibuhnk ungenuegend
mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit
)

@mutetella: Das was du "theme" nennst, würde man AFAIK eher als "topic" bezeichnen. Wobei laut Online-Wörterbuch "theme" genau so gut passen würde. Für mich klingt es in dem Zusammenhang jedenfalls besser, "topic" zu sagen.
OOP selbst ist relativ leicht zu verstehen. Insbesondere dann, wenn man Vererbung erstmal außen vorlässt. Es wird eigentlich erst dann wirklich komplex, wenn man die besagte Vererbung nutzt und wenn man die Funktionalität auf Basis bestimmter Protokolle aufbaut. Im hier gezeigten Beispiel wurde das Protokoll für Iteratoren mittels ``__iter__`` implementiert. Häufig findet man auch Implementationen von ``__str__`` oder ``__repr__``. Das sieht anfangs wie Voodoo aus (ist es ja eigentlich auch ^^), aber mit der Zeit gewöhnt man sich daran und beginnt, die entsprechenden Vorteile zu erkennen und sinnvoll zu nutzen. Ob man die Klasse ``Tasks`` in seiner vorgeschlagenen Form wirklich so benötigt oder ob man nicht direkt ein Set (also ohne die "Hülle") benutzen möchte, sei übrigens mal dahingestellt...Xfd7887a hat geschrieben:Ich glaube, ich werde mich erstmal gründlicher in die OOP einarbeiten. Das ist doch komplizierter als ich dachte und ich weil mein Programm nicht "verschlimmbessern".
@snafu
Geb' ich Dir Recht, `topic` finde ich auch besser... Wobei ich mir nicht sicher bin, ob ich eine `task`-Klasse überhaupt in dem Maße spezialisieren würde, muss aber auch zugeben, dass ich noch nicht wirklich verstehe, wohin Xfd7887a überhaupt möchte...
mutetella
Geb' ich Dir Recht, `topic` finde ich auch besser... Wobei ich mir nicht sicher bin, ob ich eine `task`-Klasse überhaupt in dem Maße spezialisieren würde, muss aber auch zugeben, dass ich noch nicht wirklich verstehe, wohin Xfd7887a überhaupt möchte...
mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit
)

@snafu
Ich würde wahrscheinlich eine `Tasks` Klasse verwenden, weil ich zusammengehörende Dinge gerne in einer Klasse zusammenfasse, also z. B. `Tasks.load()`, `Tasks.filter()` etc. Natürlich ließe sich das auch auf Modulebene über ein Set oder so organisieren...
mutetella
Ich würde wahrscheinlich eine `Tasks` Klasse verwenden, weil ich zusammengehörende Dinge gerne in einer Klasse zusammenfasse, also z. B. `Tasks.load()`, `Tasks.filter()` etc. Natürlich ließe sich das auch auf Modulebene über ein Set oder so organisieren...
mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit
)

Mal ums Speichern und Laden erweitert und vollkommen ungetestet:
Hier fängt dann eine `Tasks`-Klasse IMHO an Sinn zu machen.
`Task.__str__()` und `Tasks.__iter__()` sind überarbeitet und zumindest die `__iter__()` finde ich so besser als eine Schleife mit ``yield`` zu schreiben.
Die JSON-Struktur habe ich auf oberster Ebene geringfügig geändert, und ich würde wohl auch die einzelnen Einträge lieber als Wörterbücher mit Schlüsseln statt als Liste mit ”anonymen” Elementen speichern. Das würde die Daten zwar aufblähen, aber ist immer noch besser als XML.
Edit: Ach so, und ich habe bei `Task` die Methoden `__cmp__()` und `__hash__()` implementiert, damit das `set()` bei `Tasks` mehr Sinn macht und es sich so verhält wie die ursprüngliche Lösung, dass es immer nur einen Eintrag pro Fach geben kann/darf.
Code: Alles auswählen
import json
from datetime import datetime as DateTime
DATE_FORMAT = '%Y-%m-%d'
class Task(object):
def __init__(self, subject, date, topic, grade):
self.subject = subject
self.date = date
self.topic = topic
self.grade = grade
def __cmp__(self, other):
return cmp(self.subject, other.subject)
def __hash__(self):
return hash(self.subject)
def __str__(self):
return 'Task: {0.subject} {0.date} {0.topic} {0.grade}'.format(self)
def to_list(self):
return [
self.subject,
self.date.strftime(DATE_FORMAT),
self.topic,
self.grade,
]
@classmethod
def from_list(cls, values):
subject, date, topic, grade = values
return cls(subject, DateTime.strptime(date, DATE_FORMAT), topic, grade)
class Tasks(object):
def __init__(self, tasks=()):
self.tasks = set()
self.add_tasks(tasks)
def add_task(self, task):
self.tasks.add(task)
def add_tasks(self, tasks):
for task in tasks:
self.add_task(task)
def __iter__(self):
return iter(self.tasks)
def to_dict(self):
return {'tasks': [task.to_list() for task in self]}
def save(self, filename):
with open(filename, 'w') as json_file:
json.dump(json_file, self.to_dict())
@classmethod
def from_dict(cls, values):
return cls(Task.from_list(t) for t in values['tasks'])
@classmethod
def load(cls, filename):
with open(filename, 'r') as json_file:
return cls.from_dict(json.load(json_file))
`Task.__str__()` und `Tasks.__iter__()` sind überarbeitet und zumindest die `__iter__()` finde ich so besser als eine Schleife mit ``yield`` zu schreiben.
Die JSON-Struktur habe ich auf oberster Ebene geringfügig geändert, und ich würde wohl auch die einzelnen Einträge lieber als Wörterbücher mit Schlüsseln statt als Liste mit ”anonymen” Elementen speichern. Das würde die Daten zwar aufblähen, aber ist immer noch besser als XML.

Edit: Ach so, und ich habe bei `Task` die Methoden `__cmp__()` und `__hash__()` implementiert, damit das `set()` bei `Tasks` mehr Sinn macht und es sich so verhält wie die ursprüngliche Lösung, dass es immer nur einen Eintrag pro Fach geben kann/darf.
Wie schon gesagt, ich werde mich erstmal versuchen reinzufitzen. Das gezeigte wirkt schon ziemlich kompliziert. Wahrscheinlich gewöhnt man sich wirklich dran, ich fand früher Funktionen auch kompliziert und habe nicht verstanden, warum man die nutzt.
@Xfd7887a
Ohne Dich jetzt mit einer weiteren Neuheit komplett überfordern zu wollen: Wenn ich fremden (oder oft auch meinen eigenen
) Code nicht verstehe, hilft mir das `pdb` Modul ungemein. Erstmal (und in der Regel) lässt sich das durch Einfügen von ``import pdb; pdb.set_trace()`` sehr einfach nutzen. Hier mal ein kleines Beispiel:Wenn Du eine so präparierte Funktion aufrufst, stoppt die Ausführung an jedem ``pdb.set_trace()`` Aufruf und Du landest am pdb-eigenen Prompt, an dem Du, ähnlich der Pythonshell, auf Namen, Funktionen etc. zugreifen kannst. Mit 'c' fährst Du in der Ausführung fort, mit 'q' beendest Du `pdb` und die Ausführung. In diesem Beispiel könnte ein Aufruf von `debug_example()` so aussehen:Wenn Du also mit BlackJack's Beispiel herumspielst, kannst Du mit `pdb` die Ausführung an einem Punkt, der Dir unklar ist, unterbrechen und quasi die Umgebung in Ruhe betrachten bevor Du fortfährst.
Einmal `pdb` verwendet möchte man das nie mehr missen...
Noch als Anmerkung: Die Zeile ``import pdb; pdb.set_trace()`` entspricht eigentlich nicht der gängigen Python Konvention, wird aber in der Praxis so verwendet, weil es einfach zweckmäßig ist. Und damit wurde diese "Unkonvention" zur Konvention und alles ist gut...
mutetella
Ohne Dich jetzt mit einer weiteren Neuheit komplett überfordern zu wollen: Wenn ich fremden (oder oft auch meinen eigenen

Code: Alles auswählen
def debug_example(first=None, second=None):
import pdb; pdb.set_trace()
third = first + second
import pdb; pdb.set_trace()
return third
Code: Alles auswählen
>>> debug_example('eins', 'zwei')
> <stdin>(3)debug_example()
(Pdb) first
'eins'
(Pdb) second
'zwei'
(Pdb) third
*** NameError: name 'third' is not defined
(Pdb) c
> <stdin>(5)debug_example()
(Pdb) third
'einszwei'
(Pdb) q
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 5, in debug_example
File "<stdin>", line 5, in debug_example
File "/usr/lib/python2.7/bdb.py", line 49, in trace_dispatch
return self.dispatch_line(frame)
File "/usr/lib/python2.7/bdb.py", line 68, in dispatch_line
if self.quitting: raise BdbQuit
bdb.BdbQuit
Einmal `pdb` verwendet möchte man das nie mehr missen...


mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit
)

Danke für den Tipp mutella
Ich denke mal, ich werde mir sowas http://www.python-forum.de/viewtopic.php?f=6&t=30149 bauen, aber eben erstmal in der Konsole. Das Projekt scheint sich gut für OOP-Anwendung zu eignen.

Ich denke mal, ich werde mir sowas http://www.python-forum.de/viewtopic.php?f=6&t=30149 bauen, aber eben erstmal in der Konsole. Das Projekt scheint sich gut für OOP-Anwendung zu eignen.
@Xfd7887a
Also nicht, dass ein Konsolen-Tamagotchi ungeeignet wäre... Aber weshalb bleibst Du nicht bei Deiner bereits begonnenen Aufgabenverwaltung?
mutetella
Also nicht, dass ein Konsolen-Tamagotchi ungeeignet wäre... Aber weshalb bleibst Du nicht bei Deiner bereits begonnenen Aufgabenverwaltung?
mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit
)

Ich habe mir jetzt BlackJacks Code näher angeschaut und habe ein paar Fragen:
[*]Was macht diese Funktion? Wie kann man zwei Fächer vergleichen?
[*]Gleiche Frage. In der Doku steht ja:
[*]Was sind @classmethods, in meinem Buch steht nur was von staticmethods?
[*]Was ist cls? Scheint was mit den classmethods zu tun zu haben.
Sorry für die vermutlich dummen Fragen
, aber ich tu mich gerade schwer, dass alles zu verstehen.
Code: Alles auswählen
def __cmp__(self, other):
return cmp(self.subject, other.subject)
Code: Alles auswählen
def __hash__(self):
return hash(self.subject)
Aber wie kann ich mir das jetzt vorstellen?Return the hash value of the object (if it has one). Hash values are integers. They are used to quickly compare dictionary keys during a dictionary lookup.
[*]Was sind @classmethods, in meinem Buch steht nur was von staticmethods?
[*]Was ist cls? Scheint was mit den classmethods zu tun zu haben.
Sorry für die vermutlich dummen Fragen

Das Leben ist wie ein Tennisball.