Ein paar Anmerkungen:
Zum Stil:
Die Klammern hier bewirken grade mal gar nichts. Du kannst sie weglassen. Oder, falls du Python < 3 verwendest, lass sie stehen und schreib das Wort
object dazwischen. Dann bekommst du eine
New Style Class. Vermutlich möchtest du das.
Accessor-Methoden sind nicht pythonisch. Also dies hier:
Code: Alles auswählen
def __init__(self, name, schueler, stunden):
...
self.noch_nicht_gesetzt = True
def setzen(self):
self.noch_nicht_gesetzt = False
def rausnehmen(self):
self.noch_nicht_gesetzt = True
Na gut, es sind nicht wirklich Accessoren, da ja kein Wert übergeben oder abgefragt wird. Ich bezweifle trotzdem den Wert dieser Art der Programmierung. Für mich sieht sowas immer danach aus, als wolle jemand eine Art Haut oder Isolierschicht um die Interna einer Klasse wickeln, um diese vor der bösen Welt zu bewahren. Ich persönlich finde das - gerade in Python - unnötig. Es verkompliziert die Programmlogik, ohne wirklichen Gewinn, da ja folgendes immer noch möglich ist:
Außerdem finde ich es immer etwas sonderbar, wenn ich Code mit Verneinungen im Namen lesen muss. Oder anders ausgedrückt: warum beim Namen stehen bleiben? Man kann sie wunderbar zur code obfuscation benutzen:
Da muss man viel zu viel nachdenken. Mach es lieber so:
Code: Alles auswählen
def __init__(self, name, schueler, stunden):
...
self.gesetzt = False
Solltest du irgendwann einmal feststellen, dass bei jeder Zuweisung mehr Code (zB. zur Validierung oä.) benötigt wird, ändere es mittels
property:
Code: Alles auswählen
def __init__(self, name, schueler, stunden):
...
self._gesetzt = False # der Unterstrich an Anfang markiert es als privates Attribut.
@property
def gesetzt(self):
return self._gesetzt
@gesetzt.setter
def gesetzt(self, value):
if value in (True, False):
self._gesetzt = value
else:
raise ValueError('Mann Alter, hier werden nur boolean values akzeptiert, aber nicht %s!!!' % str(value))
Verwenden tut man es dann so:
Code: Alles auswählen
k = Kurs(...)
print k.gesetzt
k.gesetzt = True
print k.gesetzt
k.gesetzt = 'hallo'
Ergebnis:
Code: Alles auswählen
False
True
Traceback (most recent call last):
File "stdplankurs.py", line 23, in <module>
k.gesetzt = 'hallo'
File "stdplankurs.py", line 14, in gesetzt
raise ValueError('Mann Alter, hier werden nur boolean values akzeptiert, aber nicht "%s"!!!' % str(value))
ValueError: Mann Alter, hier werden nur boolean values akzeptiert, aber nicht "hallo"!!!
Verwende die Standard Lib, dafür ist sie da. Statt
Code: Alles auswählen
def schueler_ok(self,datum):
for schueler in self.schueler:
if not schueler.klausur_setzen_ok(datum):
return False
return True
mach es lieber so:
Code: Alles auswählen
def schueler_ok(self, datum):
return all(schueler.klausur_setzen_ok(datum) for schueler in self.schueler)
Auch hier wird Verneinung vermieden: man muss nicht fragen, welche Eigenschaft allen Schüler nicht fehlt.
Nun zur Programmstruktur:
Kurs.__init__() erwartet eine Liste von Schülern, also von Objekten der Klasse Schueler, die folglich bereits erzeugt worden sein müssen, bevor ein Kurs-Objekt erzeugt werden kann. Schueler.__init__() erwartet eine Liste von Kursen, also Kurs-Objekten, die folglich bereits erzeugt worden sein müssen, bevor ein Schueler-Objekt erzeugt werden kann. Du siehst das Problem?
[EDIT] Gerade sehe ich, dass in Schueler.__init__() zwar eine Referenz auf die Liste der Kurse gespeichert wird, die Kurse aber erst später verwendet werden. Die Liste kann also zu diesem Zeitpunkt noch leer sein. Damit ist mein letzter Punkt hinfällig. Ich würde es trotzdem auslagern, so wie unten beschrieben. [/EDIT]
Die Lösung: m:n-Beziehungen als eigenständige Objekte verwalten, zB. als Menge von Paaren
(Schueler, Kurs) oder einfach als Zuordnung von Schülern zur Liste der Kurse, die sie belegt haben:
Code: Alles auswählen
from collections import defaultdict
schueler_kurse = defaultdict(list) # Schueler --> Kurse
for schueler in schuelern:
for kurs in kursen:
if schueler hat diesen kurs belegt: # diese Daten irgendwo herholen
schueler_kurs[schueler].append(kurs)
Das bringt einige Vorteile. ZB. den, dass man nicht mehr die Berechnungen für jeden einzelnen Schüler vornehmen muss, sondern bloß noch für die Fächerkombinationen, die die Schüler gewählt haben, wobei es ja sein kann, dass mehrere Schüler dieselben Kurse besuchen. Das kann man dann, das o.s. Programm vorausgesetzt, so machen:
Code: Alles auswählen
faecher_kombis = set(sorted(kurse) for kurse in schueler_kurse.itervalues())
Danach kann man dann für jedes Element dieser Menge alle Kombination von möglichen Terminen berechnen, die man dann bloß noch gegen jede andere Kombination von Terminen filtern muss, und was übrig bleibt, sind die global möglichen Termine aller Klausuren. Beachte bitte die Ironie, die in den Worten
"bloß noch" oben steckt.
Ich habe bei alldem noch gar nichts von Klassen geschrieben, weil ich der Überzeugung bin, dass sich die statische Struktur eines Programms (Klassen und Module) nach den algorithmischen Erfordernissen richten sollte, nicht die Logik nach der Klassenstruktur.
Gruß,
Mick.
In specifications, Murphy's Law supersedes Ohm's.