Objektorientiertes Programmieren Zeit

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.
Sconine
User
Beiträge: 49
Registriert: Montag 1. Juni 2009, 11:00

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?
Benutzeravatar
helduel
User
Beiträge: 300
Registriert: Montag 23. Juli 2007, 14:05
Wohnort: Laupheim

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
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

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
Das Leben ist wie ein Tennisball.
Sconine
User
Beiträge: 49
Registriert: Montag 1. Juni 2009, 11:00

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?
Sconine
User
Beiträge: 49
Registriert: Montag 1. Juni 2009, 11:00

OK, die anderen Tipps werde ich mir jetzt auch zu Herzen nehmen. DAuert alles etwas länger bei mir. :(
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

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.
Das Leben ist wie ein Tennisball.
Benutzeravatar
Dill
User
Beiträge: 470
Registriert: Mittwoch 10. Januar 2007, 14:52
Wohnort: Köln

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
http://www.kinderpornos.info
Benutzeravatar
Dill
User
Beiträge: 470
Registriert: Mittwoch 10. Januar 2007, 14:52
Wohnort: Köln

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) :twisted:
http://www.kinderpornos.info
Sconine
User
Beiträge: 49
Registriert: Montag 1. Juni 2009, 11:00

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!!!
Benutzeravatar
bwbg
User
Beiträge: 407
Registriert: Mittwoch 23. Januar 2008, 13:35

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
Sconine
User
Beiträge: 49
Registriert: Montag 1. Juni 2009, 11:00

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. :(
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Ein dicker Fehler ist noch drin:

Code: Alles auswählen

>>> t = Time(13,37,61)
>>> print t
13:37:61
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.
Zuletzt geändert von snafu am Montag 15. Juni 2009, 12:26, insgesamt 2-mal geändert.
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

"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:

Code: Alles auswählen

t1 = Time(...)
t2 = Time(...)

t = t1 + t2
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.
Das Leben ist wie ein Tennisball.
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Ja, Bequemlichkeit. ;)

Korrekterweise macht man aus dem `assert` ein `if not:` und wirft einen TypeError.
Sconine
User
Beiträge: 49
Registriert: Montag 1. Juni 2009, 11:00

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.
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

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
Das Leben ist wie ein Tennisball.
Benutzeravatar
Dill
User
Beiträge: 470
Registriert: Mittwoch 10. Januar 2007, 14:52
Wohnort: Köln

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?
http://www.kinderpornos.info
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

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.
Sconine
User
Beiträge: 49
Registriert: Montag 1. Juni 2009, 11:00

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.
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

`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:

Code: Alles auswählen

>>> Foo.say_bar(Foo())
'bar'
Das ist aber unüblich und soll nur der Veranschaulichung dienen.
Zuletzt geändert von snafu am Montag 15. Juni 2009, 14:32, insgesamt 2-mal geändert.
Antworten