Was geht noch einfacher?

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.
rolgal_reloaded
User
Beiträge: 312
Registriert: Dienstag 24. Oktober 2006, 19:31

Hier mal der Versuch ein TUI mit einer Klasse zu machen:
Gesamter Code inkl. DataBook.


http://www.ubuntuusers.de/paste/11393/


Besonders stört mich selbst noch, dass folgende Methode der Klasse TUI_DataBook, die Anzahl der Einträge überprüft. Das scheint mir nicht so schön.
Mir ist aber noch keine wirkliche gelungene Alternative eingefallen.
Soll ich vielleicht eine Error- und Sucessmessage in DataBook definieren und die von über return an die untenstehende Methode zurückgeben?

Code: Alles auswählen

    def enter(self):
        number = 0
        values = []
        for key in self.keys:
            entry = raw_input("%s: " % key.capitalize())
            if entry:
                values.append(entry)
                number += 1
            else:
                values.append("")
                
        if number >= self.instance.min_entries:
            self.instance.add_entry(values)
            print "Entry added"
        else:
            print "You have to make at least %s entries" % self.instance.min_entries


Für sonstige Hinweise, Kommentare jeder Art bin ich wie immer auch dankbar.


LG

rolgal_reloaded
BlackJack

Das man so etwas mit einer Ausnahme, zum Beispiel `ValueError` im `DataBook` beantwortet, ist wahrscheinlich für Deine Zielgruppe angeblich wieder zu kompliziert. ;-)
rolgal_reloaded
User
Beiträge: 312
Registriert: Dienstag 24. Oktober 2006, 19:31

BlackJack hat geschrieben:Das man so etwas mit einer Ausnahme, zum Beispiel `ValueError` im `DataBook` beantwortet, ist wahrscheinlich für Deine Zielgruppe angeblich wieder zu kompliziert. ;-)
Mach dich ruhig lustig,.....
Ausserdem solltest du gelesen haben, dass ich zur Zeit dieses Beispiel noch nicht für meine Zielgruppe sehe, das kann sich aber auch mit der Zeit, die gute Ideen zur didaktischen Aufarbeitung bringt ändern.

Zum Inhalt:
Wieso mit einem ValueError? Meinst du mit raise ValueError - ?

Code: Alles auswählen

    def add_entry(self, values):
        if sum(1 for value in values if value) >= self.min_entries:
            data = {}
            for i, key in enumerate(self.keys):
                data[key] = values[i]
            self.entries.append(data)
        else:
            raise ValueError("Minimum Entries %s" % self.min_entries)
Dann ist das Programm beendet, irgendwie fände ich es hübscher wenn das Programm weiterläuft, oder verstößt das wieder gegen die zehn Gebote des guten Code :wink:

Oder ich habe es noch nicht verstanden, was du meinst, guuut möglich, kennen wir ja:-))

LG

rolgal_reloaded
BlackJack

`ValueError` weil das IMHO von den Standard-Ausnahmen am besten passt -- der Wert des Arguments entspricht nicht den Vorgaben.

Damit sich das Programm nicht beendet, sollte man die Ausnahme natürlich an der entsprechenden Stelle im Programm mit ``try``/``except`` behandeln. Und den Benutzer darüber informieren und ihm am besten den Eintrag nochmal zum bearbeiten vor die Nase setzen.
rolgal_reloaded
User
Beiträge: 312
Registriert: Dienstag 24. Oktober 2006, 19:31

BlackJack hat geschrieben:`ValueError` weil das IMHO von den Standard-Ausnahmen am besten passt -- der Wert des Arguments entspricht nicht den Vorgaben.

Damit sich das Programm nicht beendet, sollte man die Ausnahme natürlich an der entsprechenden Stelle im Programm mit ``try``/``except`` behandeln. Und den Benutzer darüber informieren und ihm am besten den Eintrag nochmal zum bearbeiten vor die Nase setzen.
Sowas habe ich mir eigentlich gedacht, heisst im Idealfall vorgestellt, aber ich habe schon ein except, das mit return eine Meldung zurückgibt, die eigentlich aber nicht zu sehen ist. Irgendwas habe ich im Detail noch nicht geschnallt.

Vielleicht check ich es ja noch.

Das mit dem ValueError leuchtet ein.

Danke,

rolgal_reloaded
rolgal_reloaded
User
Beiträge: 312
Registriert: Dienstag 24. Oktober 2006, 19:31

...also mit try....except müsste es so aussehen, oder?

Code: Alles auswählen

    def add_entry(self, values):
        try:
            if sum(1 for value in values if value) >= self.min_entries:
                data = {}
                for i, key in enumerate(self.keys):
                    data[key] = values[i]
                self.entries.append(data)
                return "Entry added!"
            else: raise ValueError
        except ValueError:
            return "Minimum Entries %s" % self.min_entries
Aber der Aufruf dieser Methode muss dann an eine Variable gebunden sein und deren Inhalt muss dann mit print ausgegeben werden.
Da gibts wohl keine andere Möglichkeit?

LG

r_r
BlackJack

Das macht doch gar keinen Sinn die Ausnahme dort zu behandeln. Dann hätte man doch gleich die ``return``\s in die ``if``- und ``else``-Zweig unterbringen können. Die Ausnahme ist ein Signal für den Aufrufer und im Gegensatz zu einem einfachen Rückgabewert für Erfolg oder Misserfolg nicht zwangsläufig für den *direkten* Aufrufer, sondern für irgendeinen in der Aufruferkette, der sich verantwortlich fühlt die Ausnahme zu behandeln.
rolgal_reloaded
User
Beiträge: 312
Registriert: Dienstag 24. Oktober 2006, 19:31

BlackJack hat geschrieben:..... Die Ausnahme ist ein Signal für den Aufrufer und im Gegensatz zu einem einfachen Rückgabewert für Erfolg oder Misserfolg nicht zwangsläufig für den *direkten* Aufrufer, sondern für irgendeinen in der Aufruferkette, der sich verantwortlich fühlt die Ausnahme zu behandeln.
:?: - liegt wohl an Wochentag und Uhrzeit

Wenn dies nicht der richtige Ort ist, dann bleiben nur 2 andere Möglichkeiten.
TUI_DataBook, oder das Hauptprogramm. Ersteres finde ich nicht sinnvoll.

Also?

Code: Alles auswählen

        elif choice == "i":
            values = app.enter()
            try:
                if sum(1 for value in values if value) >= adressbuch.min_entries:
                    adressbuch.add_entry(values)
                else:
                    raise ValueError
            except ValueError:
                print "Minimum Entries must be %s" % adressbuch.min_entries
LG

r_r
BlackJack

Es liegt wohl an der Uhrzeit. Es macht keinen Sinn die Ausnahme an dem Ort zu behandeln wo sie ausgelöst wird! Das sollte man doch eigentlich *sehen*, dass das so einfach nur unnötiger Schreibaufwand ist!

Da die minimale Anzahl zur `DataBook`-Klasse gehört, sollte sie auch dort überprüft werden. Und wenn die Bedingung nicht erfüllt ist, dann wird dort die Ausnahme ausgelöst.

Wenn Du ``int('fortytwo')`` ausführst, dann wird innerhalb der `int()`-Funktion geprüft, ob es sich bei der Zeichenkette um eine Zahl handelt und wenn nicht, wird in der Funktion ein `ValueError` ausgelöst. Und der wird nicht da drin wieder behandelt und in einen Rückgabewert umgewandelt. Dann würde man "aussen" ja gar nichts davon mitbekommen.

Die `add_entry()`-Methode kann dann also die Ausnahme auslösen, also muss man die *aussherhalb* der Methode irgendwo auf dem "Weg nach draussen" abfangen, wenn man nicht möchte, dass das Programm beendet wird.

Code: Alles auswählen

        elif choice == "i":
            values = app.enter()
            try:
                adressbuch.add_entry(values)
            except ValueError, error:
                print error
rolgal_reloaded
User
Beiträge: 312
Registriert: Dienstag 24. Oktober 2006, 19:31

@BlackJack

Die Sache mit Fehler-, Ausnahmenbehandlung muss ich mir noch intensiver anschauen.

Für den Fall hier dürfte jetzt alles klar sein:

Code: Alles auswählen

    def add_entry(self, values):
        if sum(1 for value in values if value) >= self.min_entries:
            data = {}
            for i, key in enumerate(self.keys):
                data[key] = values[i]
            self.entries.append(data)
        else:
            raise ValueError("Minimum Entries must be %s" % self.min_entries)
Also wie schon gehabt und im Hauptprogramm die Behandlung wie von dir gezeigt.

@edit:
Dann muss ich das mit der Methode load natürlich auch entsprechend ändern.



Vielen Dank nochmal,

rolgal_reloaded
rolgal_reloaded
User
Beiträge: 312
Registriert: Dienstag 24. Oktober 2006, 19:31

So, hier ist die neueste Fassung, in der ich versuchte einen Kompromiss aus den verschiedensten Ansrpüchen, Möglichkeiten, Zielsetzungen etc. zu finden.

Gleich vorweg: ich wollte eigentlich show_result noch umschreiben bevor ich es poste, das geht eleganter, habs vergessen, egal.....beim nächsten mal ist es dabei.

Grundsätzlich habe ich eine Menge geändert. Das TUI als Klasse zu implementieren habe ich verworfen, aber warum nicht mal was versuchen.



http://www.ubuntuusers.de/paste/11499/

Für Kommentare jeder Art, sofern sie konstruktiv sind, bin ich wie immer dankbar.

@edit:
Was mir selber gleich einfällt poste ich hier, dann braucht man auf das nicht mehr hinzuweisen.

1.

Code: Alles auswählen

    def search(self, keyword):
        return [data for data in self.entries if keyword in data.itervalues()]
2.

Code: Alles auswählen

    def sort_entries(self):
        return sorted(self.entries, key=lambda x: x[self.keys[0]])
Ist wohl kompakter, denke ich.


LG

rolgal_reloaded
rolgal_reloaded
User
Beiträge: 312
Registriert: Dienstag 24. Oktober 2006, 19:31

Zur Methode show_result():

Leider habe ich das nicht wirklich so viel schöner lösen können wie ich dachte, einfach eine neue Liste erstellen und die dann über " ".join() zurückgeben ist ja auch nicht so viel besser, oder übersehe ich da einen wesentlichen Vorteil?

Mir wäre eher folgendes vorgeschwebt:

Die Dictionaries in der Liste an Ort und Stelle fertig umändern und dann diese zurückgeben, aber irgendwie will das nicht....

LG

r_r
rolgal_reloaded
User
Beiträge: 312
Registriert: Dienstag 24. Oktober 2006, 19:31

:?: :?: Die Vorschläge und Kommentare sind ja gewaltig !!!

Wird der Thread boykottiert :roll: ?

Es ist schon alles perfekt, das muss es sein :wink:

Oder liegt es daran, dass die Diskussion mit mir selbst, effizient genug ist 8)

:arrow: :arrow: :arrow:
rolgal_reloaded
User
Beiträge: 312
Registriert: Dienstag 24. Oktober 2006, 19:31

rolgal_reloaded hat geschrieben: Mir wäre eher folgendes vorgeschwebt:

Die Dictionaries in der Liste an Ort und Stelle fertig umändern und dann diese zurückgeben, aber irgendwie will das nicht....

r_r
@rolgal_reloaded :D 8)

So geht das natürlich im Kontext mit dem Code nicht, weil es dann z. Bsp. beim Aufruf von search() knallt! Aber das hier ist vielleicht eine hübsche Variante:

Code: Alles auswählen

def show_result(self, result=None):
    if result == None:
        result = self.entries
    return "\n".join(data["index"] + " " + " ".join((data[key] for key in self.keys)) \
                     for data in result)
LG

rolgal_reloaded
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

rolgal_reloaded hat geschrieben:

Code: Alles auswählen

    if result == None:
Hallo rolgal_reloaded!

Dann durchbreche ich hiermit mal deinen Monolog. :D

Man sollte None nicht mit ``==``, sondern mit ``is`` vergleichen. Warum -- weiß ich nicht mehr, denn es funktioniert in jedem Fall auch mit ``==``. Also steckt eher etwas Esoterisches dahinter. Unsere Sprachwissenschaftler können dir sicher genauer sagen, warum das so ist. :wink:

mfg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
mitsuhiko
User
Beiträge: 1790
Registriert: Donnerstag 28. Oktober 2004, 16:33
Wohnort: Graz, Steiermark - Österreich
Kontaktdaten:

gerold hat geschrieben:Man sollte None nicht mit ``==``, sondern mit ``is`` vergleichen. Warum -- weiß ich nicht mehr, denn es funktioniert in jedem Fall auch mit ``==``. Also steckt eher etwas Esoterisches dahinter. Unsere Sprachwissenschaftler können dir sicher genauer sagen, warum das so ist. :wink:
Ganz einfach: == kann man überladen, is nicht:

Code: Alles auswählen

>>> class Foo(object):
...  def __cmp__(self, other):
...   return 0
... 
>>> Foo() == None
True
Und ganz nebenbei ist "is" auch noch schneller, weil in jedem Fall die Speicheradressen verglichen werden.
TUFKAB – the user formerly known as blackbird
rolgal_reloaded
User
Beiträge: 312
Registriert: Dienstag 24. Oktober 2006, 19:31

@gerold

Das finde ich schön, dass du den Monolog unterbrichst. Danke für den Tipp, das werde ich dann gleich ausbessern, Demnächst werde ich mal die akutelle Fassung im Gesamten posten.

Übrigens: ich brauche vielleicht noch eine statische Methode. Weisst du mehr über statische Methoden ? (rhetorische Frage :D) Ich habe die eigentlich erst durch BJ Beispiel ziemlich am Anfang des Threads kennengelernt.
Wann sollte man sie auf keinen Fall implementieren usw. ?

LG

rolgal_reloaded
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

rolgal_reloaded hat geschrieben:Weisst du mehr über statische Methoden? [...] Wann sollte man sie auf keinen Fall implementieren usw.?
Hallo rolgal_reloaded!

Mit statischen Methoden kannst du zum Beispiel Funktionen, die irgendwie zusammen gehören, in einer Klasse zusammenfügen. Damit hast du dann mehrere allein stehende Funktionen, die eigentlich auch ohne die Klasse funktionieren würden, in einen Namensraum zusammen gefasst. Sozusagen als organisatorische Zusammenfassung. Diese statischen Funktionen können aufgerufen werden, ohne vorher eine Instanz einer Klasse zu erstellen. Es wird kein ``__init__()`` aufgerufen. ``self`` fällt weg, da es ja keinen Bezug zu einer Klasseninstanz gibt.

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: iso-8859-15 -*-


class TextTools(object):
    
    @staticmethod
    def center_block(text):
        max_lenght = max( len(line) for line in text.splitlines() )
        lines = []
        for line in text.splitlines():
            lines.append(line.center(max_lenght))
        return "\n".join(lines)
    
    
    @staticmethod
    def lower_first(word):
        word = word.title()
        word = word.swapcase()
        return word


def main():
    s = (
        "Das ist die perfekte Welle.\n"
        "Das ist der perfekte Tag.\n"
        "Wir sind gekommen um zu bleiben."
    )
    print TextTools.center_block(s)
    print
    print TextTools.lower_first("Hallo")


if __name__ == "__main__":
    main()
Der große Vorteil von Klassen ist ja der, dass man **Objekte** des täglichen Lebens abbilden kann. Daten und Methoden gehören zusammen. Methoden machen meist irgend etwas mit den Daten. Wenn eine Funktion nichts mit den Daten oder mit dem abgebildeten **Objekt** macht oder auch nicht auf diese Daten zugreift, dann ist es auch nicht zwingend erforderlich, diese Funktion an die Klasseninstanz zu binden. In diesem Fall könnte eine statische Methode sinnvoll sein. Statische Methoden sollten aber doch etwas mit der Klasse zu tun haben. Es macht keinen Sinn, die Funktion ``fahre_auto()`` in die Klasse ``Haus`` zu geben. Die Funktion ``baue_haus()`` würde aber zur Klasse ``Haus`` passen. Erst recht dann, wenn das Resultat der Funktion ``baue_haus()`` eine neue Instanz der Klasse ``Haus`` ist.

Besser kann ich es nicht erklären. Leider. :roll: Ich bin nicht so gut in so etwas.

mfg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
Benutzeravatar
birkenfeld
Python-Forum Veteran
Beiträge: 1603
Registriert: Montag 20. März 2006, 15:29
Wohnort: Die aufstrebende Universitätsstadt bei München

Nun, da `None` ein Singleton ist, lohnt es nicht für den Vergleich die teurere `__eq__`-Maschinerie anzuwerfen. Der Vergleich mit `is` ist schneller und konzeptuell auch sinnvoller.
Dann lieber noch Vim 7 als Windows 7.

http://pythonic.pocoo.org/
rolgal_reloaded
User
Beiträge: 312
Registriert: Dienstag 24. Oktober 2006, 19:31

@gerold

Danke für die ausführliche Erklärung, auch wenn das meiste davon klar war, ist es gut auf den Punkt gebracht. Sowas schadet ja nicht.

Gibts diese statischen Methoden erst seit kurzem? Vor längerer Zeit habe ich mir nämlich mal erlaubt eine Klasse auch, genau wie du es genannt hast als organisatorische Zusammenfassung zu verwenden.
Bumm, da gabs nen Rüffel von einem unserer Meister hier - wer das wohl war, ggg.
Ich konnte seiner Argumentation auch folgen, aber der Tipp, es wenn dann mit statischen Methoden zu machen, wäre vielleicht auch nicht schlecht gewesen. Aber vielleicht gab sie sie auch nicht, deshalb die Frage nach dem "Alter" der statischen Methoden.

Bez der statischen Methdoden wollte ich vorher abklären, ob es besondere Aspekte zu berücksichtigen gibt. Dann spar ich mir das große Fragen "Warum funktioniert das nicht....." und euch das große Antworten.

Ganz konkret: Wenn load() in meinem DataBook eine statische Methode ist, dann wäre es ja wohl nicht so verkehrt save() auch zu einer solchen zu machen, oder?

Code: Alles auswählen

    @staticmethod
    def load(path):
        my_file = file(path, "rb")
        entries = pickle.load(my_file)
        my_file.close()
        return entries

    @staticmethod
    def save(path):
        my_file = file(path, "wb")
        pickle.dump(my_file)
        my_file.close()  
LG

rolgal_reloaded

P.S:
Besser kann ich es nicht erklären. Leider. Rolling Eyes Ich bin nicht so gut in so etwas.
Da schätzt du dich falsch ein, würde ich mal sagen :D
Antworten