Seite 1 von 2
Objektorientiertes Programmieren Zeit
Verfasst: Montag 15. Juni 2009, 09:28
von Sconine
Hallo ihr,
es geht heut wiexder um Objektorientiertes Programmieren.
Ich habe in dem Programm schon die leeren returns weggelassen, (versucht) auf Groß- und Kleinschreibung zu achten und glaube, dass man noch mehr "besser" machen kann, was mein Mitstudent nicht behauptet.
Code: Alles auswählen
class Zeit:
"""Klasse fuer Zeiten und Operationen damit"""
def __init__(self,stunden=0,minuten=0,sekunden=0):
"""fuellt die Attribute des Objektes mit der uebergebenen Zeit.
Benutzt 0 wenn kein Wert uebergeben wurde."""
self.stunden = stunden
self.minuten = minuten
self.sekunden = sekunden
#return
def ausgeben(self):
"""gibt die Zeit in einem Format zuerueck,
das gut ausgegeben werden kann"""
return self.stunden,":",self.minuten,":",self.sekunden
def in_sekunden(self):
"""Rechnet die Zeit des Objektes in Sekunden um
und gibt den Sekundenwert zurueck"""
s=self.stunden*3600+self.minuten*60+self.sekunden
return s
def sek_to_time(self,sek):
"""Der Funktion wird eine Sekundenzahl uebergeben.
Sie wird in das klassische Format umgerechnet,
als Attribut der Klasse gespeichert und zurueckgegeben"""
h, restsekunden = divmod(sek,3600)
m, s = divmod(restsekunden, 60)
self.stunden, self.minuten, self.sekunden = h, m, s
return h, m, s
def add(self,zweite_zeit):
"""Addiert die Zeit eines uebergebenen Objektes zur
Zeit des eigenen Objektes und gibt die Summe zurueck"""
ges_sekunden = self.in_sekunden() + zweite_zeit.in_sekunden()
h, restsekunden = divmod(ges_sekunden,3600)
m, s = divmod(restsekunden, 60)
return h, ":", m, ":", s
def set_stunden(self,neue_stunden):
"""Setzt das Stundenattribut des Objektes auf uebergebenen Wert"""
self.stunden = neue_stunden
#return
def set_minuten(self,neue_minuten):
"""Setzt das Minutenattribut des Objektes auf uebergebenen Wert"""
self.minuten = neue_minuten
#return
def set_sekunden(self,neue_sekunden):
"""Setzt das Sekundenattribut des Objektes auf uebergebenen Wert"""
self.sekunden = neue_sekunden
#return
def get_stunden(self):
"""Gibt die Stunden des Objektes zurueck"""
return self.stunden
def get_minuten(self):
"""Gibt die Minuten des Objektes zurueck"""
return self.minuten
def get_sekunden(self):
"""Gibt die Sekunden des Objektes zurueck"""
return self.sekunden
#Tests
#erstelle drei Objekte der Klasse Zeit mit unterschiedlichen Werten
unserezeit = Zeit(13,37,59)
anderezeit = Zeit(1,2,3)
drittezeit = Zeit() #noch ohne inhalt
print unserezeit.ausgeben()
print unserezeit.in_sekunden()
print unserezeit.add(anderezeit)
unserezeit.set_stunden(22)
print unserezeit.ausgeben()
unserezeit.set_minuten(22)
print unserezeit.ausgeben()
unserezeit.set_sekunden(22)
print unserezeit.ausgeben()
print anderezeit.get_stunden()
print anderezeit.get_minuten()
print anderezeit.get_sekunden()
drittezeit.sek_to_time(4845) #soll sein 4845s = 1h 20m 45s
print drittezeit.ausgeben()
Was meint ihr?
Verfasst: Montag 15. Juni 2009, 09:47
von helduel
Moin,
ich persönlich würde alles in Englisch halten, weil ja auch die Python-Funktionen alle in Englisch sind. Wenn du aber schon Deutsche Klassen- und Methodennamen nimmst, dann solltest du imo konsequent dabei bleiben und nicht Deutsch mit Englisch vermischen (sek_to_time).
Die trivialen Getter und Setter sind unnötig. Kannst sie alle wegschmeißen und auf die Attribute direkt zugreifen.
Gruß,
Manuel
Verfasst: Montag 15. Juni 2009, 10:19
von EyDu
Da geht noch viel:
- wenn du nicht schon Python 3 benutzt, dann sollte deine Klasse von "object" erben.
- nimm auch die auskommentierten "return"s raus, die sind vollkommen überflüssig
- "ausgeben" sollte die Zeit auch ausgeben und nicht nur einen String zurück geben. Wenn du einen String haben möchtest, dann überschreibe die __str__-Methode. Hier mal ein Beispiel, auch gleich mit vernünftiger Fomratierung:
Code: Alles auswählen
class Zeit(object):
...
def __str__(self):
return "%d:%d:%d" % (self.stunden, self.minuten, self.sekunden)
- bei "in_sekunden" kannst du den Wert gleich zurückgeben und musst das Ergebnis nicht vorher noch an "s" binden.
- zu add: schau dir mal die die Special-Method "__add__" an, funktioniert so ähnlich wie "__str__". Dann kannst du zeiten auch mit einem "+" addieren.
- außerdem erwartet man von einer add-Methode, dass eine neue Zeit zurückgegeben wird und nicht ein String!
- und auch wenn es sehr kleinlich ist: Benutze Konstanten wie SECONDS_PER_MINUTE und SECONDS_PER_HOUR.
- die Leerzeile zwischen Methoden-Kopf und Docstring kannst du dir sparen
- verpacke den Code auf Modulebene hinter einem "if __name__ == '__main__'", sonst kannst du dein Modul nicht sinnvoll importieren
Verfasst: Montag 15. Juni 2009, 10:31
von Sconine
Ok, das mit dem Vermischen der Sprachen hätte ich selber wissen müssen.
Code: Alles auswählen
class Zeit:
"""Klasse fuer Zeiten und Operationen damit"""
def __init__(self,hours=0,minutes=0,seconds=0):
"""fuellt die Attribute des Objektes mit der uebergebenen Zeit.
Benutzt 0 wenn kein Wert uebergeben wurde."""
self.hours = hours
self.minutes = minutes
self.seconds = seconds
def ausgeben(self):
"""gibt die Zeit in einem Format zuerueck,
das gut ausgegeben werden kann"""
return self.hours,":",self.minutes,":",self.seconds
def in_seconds(self):
"""Rechnet die Zeit des Objektes in Sekunden um
und gibt den Sekundenwert zurueck"""
s=self.hours*3600+self.minutes*60+self.seconds
return s
def s_to_time(self,s):
"""Der Funktion wird eine Sekundenzahl uebergeben.
Sie wird in das klassische Format umgerechnet,
als Attribut der Klasse gespeichert und zurueckgegeben"""
h, restseconds = divmod(s,3600)
m, s = divmod(restseconds, 60)
self.hours, self.minutes, self.seconds = h, m, s
return h, m, s
def addition(self,second_time):
"""Addiert die Zeit eines uebergebenen Objektes zur
Zeit des eigenen Objektes und gibt die Summe zurueck"""
ges_seconds = self.in_seconds() + second_time.in_seconds()
h, restseconds = divmod(ges_seconds,3600)
m, s = divmod(restseconds, 60)
return h, ":", m, ":", s
def hours(self,new_hours):
"""Setzt das Stundenattribut des Objektes auf uebergebenen Wert"""
self.hours = new_hours
def minutes(self,new_minutes):
"""Setzt das Minutenattribut des Objektes auf uebergebenen Wert"""
self.minutes = new_minutes
def seconds(self,new_seconds):
"""Setzt das Sekundenattribut des Objektes auf uebergebenen Wert"""
self.seconds = new_seconds
#return
def hours(self):
"""Gibt die Stunden des Objektes zurueck"""
return self.hours
def minutes(self):
"""Gibt die Minuten des Objektes zurueck"""
return self.minutes
def seconds(self):
"""Gibt die Sekunden des Objektes zurueck"""
return self.seconds
#Tests
#erstelle drei Objekte der Klasse Zeit mit unterschiedlichen Werten
unserezeit = Zeit(13,37,59)
anderezeit = Zeit(1,2,3)
drittezeit = Zeit() #noch ohne inhalt
print unserezeit.ausgeben()
print unserezeit.in_seconds()
print unserezeit.addition(anderezeit)
unserezeit.hours(22)
print unserezeit.ausgeben()
unserezeit.minutes(22)
print unserezeit.ausgeben()
unserezeit.seconds(22)
print unserezeit.ausgeben()
print anderezeit.hours()
print anderezeit.minutes()
print anderezeit.seconds()
drittezeit.sek_to_time(4845) #soll sein 4845s = 1h 20m 45s
print drittezeit.ausgeben()
Die setter und getter und auch eliminiert.
Wo kommt denn jetzt der Fehler her?
Code: Alles auswählen
(13, ':', 37, ':', 59)
49079
(14, ':', 40, ':', 2)
Traceback (most recent call last):
File "C:\Python25\Eigene\ZEIT_ICH.py", line 107, in <module>
unserezeit.hours(22)
TypeError: 'int' object is not callable
Hab ich zu voreilig etwas gelöscht?
Verfasst: Montag 15. Juni 2009, 10:33
von Sconine
OK, die anderen Tipps werde ich mir jetzt auch zu Herzen nehmen. DAuert alles etwas länger bei mir.

Verfasst: Montag 15. Juni 2009, 10:46
von EyDu
Lass auch die hours-, minutes- und seconds-Methoden weg, welche einfach nur die Werte zurückgeben. Wenn du an die Stunden kommen willst, dann schreibst du einfach nur "uhrzeit.hours" und greifst DIREKT auf das Attribut zu. Selbiges gilt für das Setzen. Nur wenn beim Abfragen der Zeit noch Berechnungen durchgeführt werden sollen, benutzt man eine Methode. Mit properties wird der Zugriff dann aber wieder transparent gemacht. In deinem Fall also: greife von außen direkt auf die Attribute zu. Python ist kein Java oder C++!
An den letzten Punkt lässt sich auch gleich anschließen: Du hast dreimal verschiedene Dinge an den selben Namen gebunden: das Attribut "hours", eine Methode zum Setzen der Stunden "hours" und eine zum Abfragen die ebenfalls "hours" heißt. Es kann aber nur einen Namen "hours" geben. Daher auch die Fehlermeldung, welche bei dir geworfen wird. Python erkennt keine Funktionen an Hand deren Signatur. Ein Name zeigt auf genau ein Objekt.
Verfasst: Montag 15. Juni 2009, 10:47
von Dill
du hast ein attribut und eine methode mit namen "hours".
das attribut verdeckt die methode, daher wird bei self.hours(22) folgendes versucht: (angenommen in self.hours steht "10")
Code: Alles auswählen
In [11]: type(10)
Out[11]: <type 'int'>
In [12]: 10(22)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython console> in <module>()
TypeError: 'int' object is not callable
würdest du dich an die konventionen halten, hättest du diesen fehler nicht:
"Methoden werden mit Verben benannt. (sie TUN was - TU-wort)"
in dem fall brauchst du die methode aber gar nicht, da du direkt auf das attrbut zugriefen solltest.
a_time.hours = 22
Verfasst: Montag 15. Juni 2009, 10:55
von Dill
Sconine hat geschrieben:
Die setter und getter und auch eliminiert.
du scherzkeks, da seh ich ja erst jetzt.
du hast nicht die setter/getter eliminiert, du hast sie nur umbenannt.
(set_ und get_ weggelassen)

Verfasst: Montag 15. Juni 2009, 11:19
von Sconine
Oh man, da ist man froh, dass ein Programm läuft und dann kann man doch noch so vieles ändern.

Da ich das geschriebene Programm gut verstehen konnte, dauert es mit dem "Umdenken".
Ich versuche alle Vorschläge Schritt für Schritt durchzugehen.
Ich gebe mein Bestes!!!
Verfasst: Montag 15. Juni 2009, 11:38
von bwbg
Mit dem Programmieren ist es wir mit dem Kölner Dom...
*
Es sei der Fall: z = Zeit(1, 2, 61)
*
Es sei ferner der Fall: z.minuten(90)
Um meine Mittagspause sinnvoll zu nutzen, habe ich selbst eine Time-Klasse zusammengeflickt:
http://paste.pocoo.org/show/123206/
Der Ansatz ist ein anderer, da hier lediglich die Gesamtsekunden als Attribut verwendet werden und auf die Einzelwerte (Stunden, Minuten und Sekunden) über Properties (sehr interessantes Thema übrigens) zugegriffen wird.
Grüße... Heiko
Verfasst: Montag 15. Juni 2009, 12:00
von Sconine
Das sind viele Tipps aufmal.
Alle Getter und Setter weg:
Code: Alles auswählen
class Zeit:
"""Klasse fuer Zeiten und Operationen damit"""
def __init__(self,hours=0,minutes=0,seconds=0):
"""fuellt die Attribute des Objektes mit der uebergebenen Zeit.
Benutzt 0 wenn kein Wert uebergeben wurde."""
self.hours = hours
self.minutes = minutes
self.seconds = seconds
def __str__(self):
"""gibt die Zeit in einem Format zuerueck,
das gut ausgegeben werden kann"""
return "%d:%d:%d" % (self.hours, self.minutes, self.seconds)
def in_seconds(self):
"""Rechnet die Zeit des Objektes in Sekunden um
und gibt den Sekundenwert zurueck"""
s = self.hours*3600+self.minutes*60+self.seconds
def s_to_time(self,s):
"""Der Funktion wird eine Sekundenzahl uebergeben.
Sie wird in das klassische Format umgerechnet,
als Attribut der Klasse gespeichert und zurueckgegeben"""
h, restseconds = divmod(s,3600)
m, s = divmod(restseconds, 60)
self.hours, self.minutes, self.seconds = h, m, s
return h, m, s
def __add__(self,second_time):
"""Addiert die Zeit eines uebergebenen Objektes zur
Zeit des eigenen Objektes und gibt die Summe zurueck"""
ges_seconds = self.in_seconds() + second_time.in_seconds()
h, restseconds = divmod(ges_seconds,3600)
m, s = divmod(restseconds, 60)
return h + m + s
#Tests
#erstelle drei Objekte der Klasse Zeit mit unterschiedlichen Werten
unserezeit = Zeit(13,37,59)
anderezeit = Zeit(1,2,3)
drittezeit = Zeit() #noch ohne inhalt
print unserezeit.__str__()
print unserezeit.in_seconds()
print unserezeit.__add__()
drittezeit.s_to_time(4845) #soll sein 4845s = 1h 20m 45s
print drittezeit.__str__()
Bei __add__ habe ich das gefunden:
Code: Alles auswählen
def __add__(self, other):
self = force(self)
other = force(other)
return self + other
Kann ich dort nachvollziehen, aber ich schaffe es nicht in mein Programm zu schreiben.
Geschriebene Programme kann ich nachvollziehen, aber wenn es darum geht etwas umzusetzen, verstehe ich nur Bahnhoh.

Verfasst: Montag 15. Juni 2009, 12:15
von snafu
Ein dicker Fehler ist noch drin:
Du solltest also auf jeden Fall prüfen, ob die an `__init__()` übergebenen Werte gültig sind und falls nicht, am besten einen ValueError werfen. Sonst rechnest du unter Umständen auf einer völlig falschen Basis. Und so etwas musst du eigentlich immer bedenken, wenn du Sachen "von außen" annimmst.
Auch bei `__add__()` müsstest du prüfen, ob tatsächlich eine Instanz deiner Klasse übergeben wird. Das könnte man am Anfang der Methode so in der Art machen:
Code: Alles auswählen
assert isinstance(second_time, Zeit), 'Need a Zeit-object'
Vielleicht hier mal die Klasse "verenglischen".
Analog dazu würde ich auch bei `s_to_time()` vorgehen. Hier willst du eigentlich nur Integer. Denn teste mal, was passiert, wenn man `divmod()` eine Gleitkommazahl oder gar einen String übergibt... Du müsstest halt überlegen, ob du dort "still" in Integer umwandelst oder einen Fehler wirfst. Ich glaube ich würde das Erste tun, und ggf. einen entsprechenden Hinweis in den Docstring setzen.
Verfasst: Montag 15. Juni 2009, 12:20
von EyDu
"in_seconds" muss natürlich den Wert zurückgeben und nicht in einer lokalen Variable speichern, die dann eh wieder vergessen wird. Aus dem "s = " machst du einfach ein "return ".
"s_to_time" solltest du gleich "seconds_to_time" nennen. Wie das "to_time" andeutet, sollte natürlich auch ein Time-Objekt zurückgegeben werden und kein Tupel: "return Time(h, m, s)". Außerdem solltest du die Methode entweder mit "@staticmethod" dekorieren oder gleich eine Funktion draus machen, da keine Exemplar von "Time" benötigt wird um aus Sekunden eine Zeit zu machen.
Die __add__-Method muss natürlich auch ein Time-Objekt zurück liefern. Sinnlos Stunden, Minuten und Sekunden addieren bringt nichts. "return Time(h, m, s)". Dann kannst du Zeiten so addieren:
Auch solltest du die __init__-Methode noch einmal überdenken: was ist, wenn man z.B. für die Sekunden einen Wert von > 59 eingibt? Sollte dann eine Exception geworfen werden oder werden die Minuten entsprechend angepasst?
@snafu: assert ist nur zum Debuggen und für Testläufe gedacht, nicht aber um Logik zu implementieren. Wahrscheinlich ist es dir klar, aber so weiß Sconine es auch.
Verfasst: Montag 15. Juni 2009, 12:31
von snafu
Ja, Bequemlichkeit.
Korrekterweise macht man aus dem `assert` ein `if not:` und wirft einen TypeError.
Verfasst: Montag 15. Juni 2009, 12:38
von Sconine
Ach, in Moment ist mir gar nix klar.
Werd jetzt eine kleine Pause machen, die Zeit verfliegt ja wie im Fluge, wenn man sich intensiv mit Python beschäftigt.
Das mit __add__ geht grad gar nicht in mein Kopf.
Verfasst: Montag 15. Juni 2009, 12:45
von EyDu
Ein sehr einfaches Beispiel zu __add__:
Code: Alles auswählen
>>> class GrafZahl(object):
... def __init__(self, wert):
... self.wert = wert
... def __add__(self, andere_zahl):
... return GrafZahl(self.wert + andere_zahl.wert)
... def __str__(self):
... return "GrafZahl: %d" % self.wert
...
>>> x = GrafZahl(23)
>>> y = GrafZahl(42)
>>> print x
GrafZahl: 23
>>> print y
GrafZahl: 42
>>> z = x + y
>>> print z
GrafZahl: 65
Verfasst: Montag 15. Juni 2009, 12:49
von Dill
ich habe den eindruck, dass dir noch ein paar grundlagen nicht klar sind.
Verstehst du den Grafzahl?
was ist "self", was tut das?
was ist "andere_zahl" in __add__ bzw was sollte es sinnvollerweise sein?
Verfasst: Montag 15. Juni 2009, 12:51
von snafu
Dein `s_to_time()` würde ich ja eher `seconds_to_time()` nennen (möglichst Abkürzungen vermeiden) und gar nichts an den Attributen ändern, sondern einfach eine neue `Time()`-Instanz zurückgeben, die mit h, m, s initialisiert wurde. Wenn man nur die Zeit angezeigt bekommen will, geht in dem Fall ja trotzdem noch ein `print seconds_to_time(1234567890)`, da einfach die `__str__()`-Methode der neuen Instanz aufgerufen wird.
Verfasst: Montag 15. Juni 2009, 13:25
von Sconine
Ich habe das Beispiel Grafzahl verstanden.
self.wert ist bei mir = self.hours, self.minutes, self-seconds
andere_zahl.wert ist bei mir second_time.hours, second_time.minutes, second_time. seconds
Ist es soweit richtig?
Ich muss ja hours und minutes in seconds umrechnen, damit ich doch überhaupt rechnen kann. Da habe ich meine Probleme.
Mit meinen eigenen "Namen" habe ich Probleme. Man ist das peinlich.
Self muss immer vorne in der Klammer stehen. Was es aber genau macht, weiss ich nicht. Ich weiss nur, dass es beim objekorientierten Programmieren wichtig ist.
Verfasst: Montag 15. Juni 2009, 13:45
von snafu
`self` repräsentiert die Instanz deiner Klasse. `self.seconds = 60` heißt: "Weise dem Attribut `seconds` der "aktuellen" Klasseninstanz den Wert `60` zu" Die Instanz ist immer das erste Objekt, das den Klassenmethoden übergeben wird.* Man kann es beliebig benennen aber allgemein hat sich `self` eingebürgert und sollte, um Missverständnisse zu vermeiden, auch so übernommen werden.
---
*EDIT: Bevor es zu Missverständnissen kommt, forme ich den Satz besser um: Das erste Objekt, das einer Klassenmethode übergeben wird, ist immer die Instanz der Klasse. Die Instanz ist in der Regel das, was herauskommt, wenn man sie aufruft:
Code: Alles auswählen
>>> class Foo(object):
... def say_bar(self):
... return 'bar'
...
>>> Foo.say_bar()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unbound method say_bar() must be called with Foo instance as first argument (got nothing instead)
>>> Foo().say_bar()
'bar'
Wie du siehst, kann auch auf die (meisten) Methoden einer Klasse erst zugegriffen werden, nachdem man die Klasse aufgerufen (also initialisiert) hat.
Wenn man es sich ganz umständlich machen will, könnte man die geforderte Instanz der Klasse auch so an ihre `foo()`-Methode übergeben:
Das ist aber unüblich und soll nur der Veranschaulichung dienen.