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

Hallo zusammen,

mich hat auch auf Grund eines anderen Beitrages hier die Adressbuchmania gepackt und die dort schon gepostete Skizze etwas ausgebaut.

Vorweg:
Es soll nicht wirklich als Adressbuch zum Einsatz kommen.
Ich möchte anhand dieses Code einiges demonstrieren (ne, natürlich nicht euch:-)), dabei soll die Funktionalität natürlich gegeben sein.

Was mich am meisten interessiert: was ist in welchen Funktionen noch einfacher zu formulieren. Ihr wisst ja, als Künstler, der auch viel Barockmusik gespielt hat, habe ich wohl schon von daher einen barocken Programmierstil entwickelt, lol.

Zur Handhabung: Beim Eingeben neuer Adressen einfach alles in einem Rutsch eingeben. Die Einträge dürfen unterschiedlich sein - genau das ist ein Punkt, der jetzt für mich nicht tragisch ist.
Eingabe: test test test 34 5678 test
und dann enter.

Fehler habe ich glaube ich inzwischen alle abgefangen, falls nicht bitte auch diese mitteilen.

Noch was: der Abschnitt auf die Überprüfung des Betriebssystem braucht euch nicht weiter wundern, das habe ich gemacht, damit das Skript in der Schule sowohl im Raum mit den Windowskisten wie in jenem mit den Linuxbüchsen verwendet werden kann.

So dann, auf lehrreiche Beiträge freut sich

rolgal_reloaded


Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: cp1252 -*-

##adressbuch = {1: ["Gnom","Gustav","Gnomweg", "8", "7777", "Gnomhausen"],
##              2: ["Gnom","Katharina","Gnomweg", "8", "7777", "Gnomhausen"],
##              3: ["Gnom","Gabriel","Gnomweg", "8", "7777", "Gnomhausen"]}


import pickle, sys, os


def test():
    for schluessel in adressbuch.iterkeys():
        if adressbuch[schluessel] == None:
            return schluessel
            break


def anzeigen():
    for eintrag in adressbuch.iteritems():
        if eintrag[1]:
            print eintrag[0], " ".join(eintrag[1])
        

def eintragen():
    eintraege = raw_input("Bitte Adresse eingeben: ")
    if len(eintraege.split()) >= 2:
        schluessel = test()
        if schluessel:
            adressbuch[schluessel] = eintraege.split()
        else:
            try:
                schluessel = max(adressbuch.keys()) + 1
                adressbuch[schluessel] = eintraege.split()
            except ValueError:
                adressbuch[1] = eintraege.split()
    else:
        print "Es müssen mindestens zwei Angaben gemacht werden"
        
    
def bearbeiten():
    auswahl = raw_input("Bitte die Nummer des Eintrags angeben: ")
    if auswahl:
        for i, eintrag in enumerate(adressbuch[int(auswahl)]):
            eingabe = raw_input(("Eintrag: %s neu ? --> " % eintrag))
            if eingabe:
                adressbuch[int(auswahl)][i] = eingabe        

        while True:
            eingabe = raw_input("Eintrag hinzufügen ? --> ")
            if eingabe:
                adressbuch[int(auswahl)].append(eingabe)
            else:
                break            
        

def loeschen():
    auswahl = raw_input("Bitte die Nummer des zu löschenden Eintrags eingeben: ")
    if auswahl:
        adressbuch[int(auswahl)] = None
        

def suchen():
    auswahl = raw_input("Bitte einen Begriff eingeben: ")
    treffer = []
    for eintrag in adressbuch.itervalues():
        if auswahl in eintrag:
            treffer.append(" ".join(eintrag))

    if treffer:
        print "Die Suche ergab folgende Treffer:\n%s " % "\n".join(treffer)
    else:
        print "Leider kein Eintrag gefunden"
        

def speichern():
    datei = file(pfad,"wb")
    pickle.dump(adressbuch, datei)
    datei.close()
    

def laden():
    datei = file(pfad, "rb")
    adressbuch = pickle.load(datei)
    datei.close()
    return adressbuch
    
    
    
if __name__ == "__main__":

    pfad = os.getcwd()
    
    if sys.platform == "win32":
        pfad = pfad + "//adressbuch.bin"

    elif sys.platform == "Linux1":
        pfad = pfad + "/adressbuch.bin"

    try:
        adressbuch = laden()
        print adressbuch
    except IOError:
        adressbuch = {}
        speichern()
        print "Es wurde ein neues Adressbuch erstellt!"


    print "=============Menü============="
    print "a - Adressen anzeigen"
    print "e - eintragen"
    print "b - bearbeiten"
    print "l - loeschen"
    print "s - suchen"
    print "S - speichern"
    print "E - Ende"
    print "=============================="

    while True:

        auswahl = raw_input("Auswahl: ")
        if auswahl == "a":
            anzeigen()
        elif auswahl == "e":
            eintragen()
        elif auswahl == "b":
            bearbeiten()
        elif auswahl == "l":
            loeschen()
        elif auswahl == "s":
            suchen()
        elif auswahl == "S":
            print "Adressbuch wird neu gespeichert!"
            speichern()
        elif auswahl == "E":
            antwort = raw_input("Speichern (j/n) ? ")
            if antwort == "j":
                speichern()
                break
            else:
                break
        else:
            print auswahl, "ist eine ungültige Eingabe!"

    print "Ende des Programms"
rolgal_reloaded
User
Beiträge: 312
Registriert: Dienstag 24. Oktober 2006, 19:31

Hallo nochmal,

ich glaube, so wäre es insgesamt besser, oder?
Das Programm wird im Verhalten etwas logischer, realitätsnäher und eigentlich noch etwas einfacher - so mal auf den ersten Blick behauptet.
Was meint ihr?
Jetzt nur die Datenstruktur und die Funktionen dazu:

Code: Alles auswählen

adressbuch = [{"name":"Gnom", "vorname":"Roland", "strasse":"Gnomweg 8"},
              {"name":"Gnom", "vorname":"Katharina", "strasse":"Gnomweg 8"},
              {"name":"Gnom", "vorname":"Gabriel", "strasse":"Gnomweg 8"}]

def anzeigen():
    for i, adresse in enumerate(adressbuch):
        print i + 1, adresse["name"], adresse["vorname"], adresse["strasse"]
        
def eintragen():
    keys = ["name", "vorname", "strasse"]
    adresse = {}
    for key in keys:
        eintrag = raw_input(key.capitalize() + ": ")
        adresse[key] = eintrag
    adressbuch.append(adresse)
        

def bearbeiten():
    auswahl = raw_input("Nummer: ")
    adresse = adressbuch[int(auswahl) - 1]
    print adresse
    for key in adresse.iterkeys():
        eintrag = raw_input(key.capitalize() + " " + adresse[key] + ": ")
        if eintrag:
            adresse[key] = eintrag 
        

def loeschen():
    auswahl = raw_input("Nummer: ")
    del(adressbuch[int(auswahl) - 1])

    
def suchen():
    suchbegriff = raw_input("Suche nach...? ")
    for adresse in adressbuch:
        if suchbegriff in adresse.itervalues():
            print adresse["name"], adresse["vorname"], adresse["strasse"]
 
BlackJack

Vorweg: Ich habe den Vormittag über nebenbei an einer Antwort auf den ersten Beitrag geschrieben. Mit dem zweiten haben sich einige Punkte im folgenden Text erledigt.

Das Hauptprogramm solltest Du auch in eine Funktion stecken. So ist alles im Hauptprogramm global im Modul sichtbar. Wobei ich gerade sehe, dass Du das mit Absicht tust. Das ist schlechter Stil, weil die Funktionen so nicht mehr isoliert funktionieren. Daten sollten eine Funktion möglichst nur als Argumente betreten und als Rückgabewerte verlassen.

Die `anzeigen()`-Funktion könnte man zum Beispiel benutzen, um beliebige Adresslisten auszugeben, zum Beispiel das Ergebnis der Suche. So wie sie jetzt arbeitet, gibt sie aber nur das Adressbuch aus dem Hauptprogramm aus und man muss für die Suche nochmal Quelltext schreiben, der im Grunde das macht, was man in `anzeigen()` schon einmal programmiert hat.

Das ``break`` am Ende der `test()`-Funktion wird nie ausgeführt weil davor ein ``return`` steht. Der Name der Funktion ist ziemlich nichtssagend. Wenn man einen passenden Namen wählt, wie zum Beispiel `hole_freien_schluessel()`, dann fällt auf, dass die Funktion nicht alles tut was sie könnte. Sie sollte wirklich einen freien Schlüssel liefern und nicht nur für den Fall, das kein als leer markierter Datensatz vorhanden ist. Wenn in der Schleife kein freier Platz gefunden wurde, dann müssen alle Nummern von 0 bis ``len(adressbuch) - 1`` vergeben sein, also ist der neue Schlüssel ``len(adressbuch)``. Das ist wesentlich einfacher als durch das Maximum aller Schlüssel auf das gleiche Ergebnis zu kommen und der ganze ``if``/``else``-Block mit dem ``try``/``except`` in `eintragen()` schrumpft von 9 auf eine Zeile. Falls die Funktionalität so bleiben soll wie sie ist, würde ich aber mindestens noch ein ``return None`` ans Ende schreiben. Das ist zwar implizit schon da, aber nicht besonders offensichtlich.

Code: Alles auswählen

def hole_freien_schluessel(adressbuch):
    for schluessel, wert in adressbuch.iteritems():
        if wert is None:
            return schluessel
    return len(adressbuch)
Bei `anzeigen()` würde ich die Indexe beseitigen. Ein sprechender Name ist verständlicher als eine 0 oder eine 1.

Code: Alles auswählen

def anzeigen(adressbuch):
    for schluessel, adresse in adressbuch.iteritems():
        if adresse is not None:
            print schluessel, ' '.join(adresse)
Mit `hole_freien_schluessel()` wie oben beschrieben, schrumpft `eintragen()` ziemlich zusammen:

Code: Alles auswählen

def eintragen(adressbuch): 
    eintraege = raw_input('Bitte Adresse eingeben: ').split()
    if len(eintraege) >= 2:
        adressbuch[hole_freien_schluessel(adressbuch)] = eintraege
    else:
        print 'Es müssen mindestens zwei Angaben gemacht werden'
Das Aufteilen der Eingabe sollte man nur an einer Stelle machen machen und nicht wie im Original an vier Stellen. Das gleiche gilt für etliche ``int(auswahl)`` im Quelltext.

`bearbeiten()` und `eintragen()` haben eigentlich eine gemeinsame Funktionalität, nämlich das Bearbeiten eines Eintrags. Der Unterschied ist eigentlich nur, ob in den Feldern schon etwas steht oder nicht. Ein Beispiel warum es ungünstig sein kann zweimal Quelltext für eine sehr ähnliche Operation zu schreiben, sieht man hier ganz gut: Man kann mit `bearbeiten()` Einträge erzeugen, die man mit `eintragen()` nicht erstellen kann, nämlich welche mit Leerzeichen.

Eine Lösung wäre es, in `eintragen()` einfach eine leere Liste einzutragen und dann `bearbeiten()` mit dem entsprechenden Schlüssel aufzurufen. Könnte so aussehen:

Code: Alles auswählen

def eintragen(adressbuch):
    schluessel = hole_freien_schluessel(adressbuch)
    adressbuch[schluessel] = list()
    bearbeiten(adressbuch, schluessel)


def eingabe_eintragsnummer(adressbuch, text):
    eingabe = raw_input(text)
    try:
        nummer = int(eingabe)
        if nummer in adressbuch:
            return nummer
        else:
            return None
    except ValueError:
        return None


def bearbeiten(adressbuch, auswahl=None):
    if auswahl is None:
        auswahl = eingabe_eintragsnummer(adressbuch,
                                         ('Bitte die Nummer des Eintrags'
                                          ' angeben: '))
    if auswahl is not None:
        adresse = adressbuch[auswahl]
        neue_adresse = list()
        for eintrag in adresse:
            eingabe = raw_input('Eintrag: %s neu ? --> ' % eintrag)
            if eingabe:
                neue_adresse.append(eingabe)
            else:
                neue_adresse.append(eintrag)

        while True:
            eingabe = raw_input('Eintrag hinzufügen ? --> ')
            if eingabe:
                neue_adresse.append(eingabe)
            else:
                if len(neue_adresse) < 2:
                    print 'Eine Adresse muss mindestens zwei Einträge besitzen'
                else:
                    break
        
        adressbuch[auswahl] = neue_adresse


def loeschen(adressbuch):
    auswahl = eingabe_eintragsnummer(adressbuch,
                                     ('Bitte die Nummer des zu löschenden'
                                      ' Eintrags eingeben: '))
    if auswahl is not None:
        adressbuch[auswahl] = None
Ich habe auch soweit wie möglich Indexzugriffe vermieden. Vor der letzten Zeile in `bearbeiten()` könnte man auch noch einmal nachfragen, ob die Änderungen wirklich gemacht werden sollen. Es fehlte eine Überprüfung ob die Eingabe eine Zahl ist und als Schlüssel im Adressbuch vorkommt. Da eine Schlüsseleingabe auch in `loeschen()` erforderlich ist, kann man das prima in eine eigene Funktion herausziehen.

Bei der `suche()` könnte man wie schon gesagt die `anzeige()` wiederverwenden:

Code: Alles auswählen

def suchen(adressbuch):
    suchbegriff = raw_input('Bitte einen Begriff eingeben: ')
    treffer = dict()
    for schluessel, adresse in adressbuch.iteritems():
        if suchbegriff in adresse:
            treffer[schluessel] = adresse

    if treffer:
        print 'Die Suche ergab folgende Treffer:'
        anzeigen(treffer)
    else:
        print 'Leider keinen Eintrag gefunden'
`laden()` und `speichern()` bekommen ihre Eingaben, Pfad und Adressbuch, nicht als Argumente.

Für den Pfad im Hauptprogramm müsste './adressbuch.bin' doch eigentlich plattformübergreifend funktionieren!? Auf jeden Fall fehlt eine Zuweisung falls die Plattform weder 'win32' noch 'Linux1' ist. Bei mir ist sie zum Beispiel 'linux2'.

Beim Menüpunkt 'E' haben beide Zweige der Nachfrage das ``break`` als gemeinsamen "Suffix". Der ``else``-Zweig besteht nur daraus, also kann man sich den auch sparen und das ``break`` im ``if``-Zweig eine ebene "ausrücken".

Das jetzt (fast) alle Funktionen das Adressbuch als erstes Argument erhalten, ist ein Hinweis, das man daraus eine Klasse machen könnte.

Die Wahl der Datenstruktur ist ja nicht unwichtig ─ wie würdest Du das Dictionary erklären? Das scheint mir eine nicht besonders logische Wahl zu sein. Ein Dictionary mit fortlaufenden Zahlen als Schlüssel ist eigentlich eine verkleidete Liste. Bei einer Liste könnte man anstelle der `None`-Markierungen, die Daten auch einfach löschen und würde so Speicherplatz sparen und das Programm etwas schneller machen, da beim Eintragen eines neuen Datensatzes die Liste nicht durchlaufen werden müsste und beim Suchen keine leeren Einträge berücksichtigt werden müssen.
rolgal_reloaded
User
Beiträge: 312
Registriert: Dienstag 24. Oktober 2006, 19:31

BlackJack hat geschrieben:Vorweg: Ich habe den Vormittag über nebenbei an einer Antwort auf den ersten Beitrag geschrieben. Mit dem zweiten haben sich einige Punkte im folgenden Text erledigt.
Trotzdem sehr interessant und lehrreich!
Das mit dem schlechten Stil (keine Parameter usw.) ist ganz klar. Warum ich das zunächst so machte und ursprgl. auch die erste Datenstruktur wählte ist nicht mehr relevant zu diskutieren, weil ich selber schon gesehen habe, dass die Nachteile überwiegen.

Nach dem genauen Studium deiner Antwort, werde ich mich um die Weiterentwicklung der zweiten Variante kümmern.

Also MegaDank inzwischen, einfach Klasse!

Liebe Grüße

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

Code: Alles auswählen

def eintragen(adressbuch):
    schluessel = hole_freien_schluessel(adressbuch)
    adressbuch[schluessel] = list()
    bearbeiten(adressbuch, schluessel)


def eingabe_eintragsnummer(adressbuch, text):
    eingabe = raw_input(text)
    try:
        nummer = int(eingabe)
        if nummer in adressbuch:
            return nummer
        else:
            return None
    except ValueError:
        return None

def loeschen(adressbuch):
    auswahl = eingabe_eintragsnummer(adressbuch,
                                     ('Bitte die Nummer des zu löschenden'
                                      ' Eintrags eingeben: '))
    if auswahl is not None:
        adressbuch[auswahl] = None
Ich habe noch eine Frage:
Wäre es nicht besser dies

Code: Alles auswählen

    auswahl = eingabe_eintragsnummer(adressbuch,
                                     ('Bitte die Nummer des zu löschenden'
                                      ' Eintrags eingeben: '))
so wie Parallelstellen nach main zu geben? Die Funktionen würden dann nur das beinhalten, was ihr Name aussagt.


LG

rolgal_reloaded
BlackJack

Das wäre wirklich besser. Stichwort "Trennung von GUI und Programmlogik". Dann könnte man später auch eine Tkinter-GUI auf die Funktionen draufsetzen, wo man den Eintrag mit der Maus auswählen kann, statt eine Zahl eingeben zu müssen.
rolgal_reloaded
User
Beiträge: 312
Registriert: Dienstag 24. Oktober 2006, 19:31

Hallo zusammen,

ich arbeite jetzt an drei Versionen des Adressbuches.
1. Wie gehabt wo alles Global im Hauptprogramm steht
2. Das Hauptprogramm in der Funktion main() und den damit verbundenen Veränderungen der anderen Funktionen und
3. eine Lösung mit einer Klasse.

Der Sinn ist, dass man die 3 miteinander vergleichen kann, und dabei die verschiedenen Möglichkeiten, besonders von 2 und 3 demonstrieren kann.
Die ersten beiden habe ich glaube ich gut hinbekommen, werde sie mal posten, hier jetzt nur Teilcode zur dritten Fassung.
Irgendwie ist da Sand im Getriebe, vielleicht habe ich zuviel prozedural progammiert in letzter Zeit:-)

Also: Die Methode anzeigen soll auch in der Lösung mit Klasse wiederverwendbar sein, soll heissen: wird die Methode suchen() aufgerufen, soll das Ergebnis mittel anzeigen() dargestellt werden können.

Das geht aber nur wenn ich anzeigen einen Parameter übergeben muss:

Code: Alles auswählen

    def anzeigen(self, eintraege):
        ausgabe = ""
        for i, adresse in enumerate(eintraege):
            ausgabe += str(i + 1)
            for key in self.keys:
                ausgabe += " "
                ausgabe += adresse[key]
            ausgabe += "\n"
        return ausgabe 
Im Hauptprogramm müsste dann der Aufruf bei Eingabe der entsprechenden Taste so aussehen:

Code: Alles auswählen

    ergebnis = adressbuch.anzeigen(adressbuch.eintraege)
    print ergebnis

Das gefällt mir aber nicht. Es wäre doch super, wenn anzeigen() mit einem voreingestellten Parameter programmiert werden könnte.

Quasi wie:

Code: Alles auswählen

def anzeigen(self, eintraege = self.eintraege):
Das geht aber nicht, eine Lösung in der Art wäre aber hübsch, dann bräuchte man nur in anderen Methoden - momentan betrittft das nur suchen - beim Aufruf den Parameter überschreiben, also so:

Code: Alles auswählen

        if self.treffer:
            print self.anzeigen(self.treffer)
Freilich funktioniert das, aber ich will eben nicht, dass ich bei anzeigen(), das sowieso immer alles zurückgibt, einen Parameter übergen muss.

Zum Abschluss noch der ganze Code, viele der noch offenen Sachen sind mir klar, aber erwähnt sie zur Vorsicht ruhig trotzdem, wenn ihr etwas seht, das ich bei der weiteren Programmierung noch berücksichtigen sollte.

Code: Alles auswählen

import pickle, os, sys

class Adressbuch:

    pfad = os.getcwd()

    def __init__(self, dateiname,
                 keys, mindestanzahl = None):

        self.keys = keys
        self.mindestanzahl = mindestanzahl
        if sys.platform == "win32":
            self.pfad = self.pfad + "//" + dateiname

        elif sys.platform == "Linux1":
            self.pfad = self.pfad + "//" + dateiname

        try:
            datei = file(self.pfad, "rb")
            self.eintraege = pickle.load(datei)
            datei.close()
        except IOError:
            self.eintraege = list()
            self.speichern(self.pfad, self.eintraege)
            print "%s wurde erstellt!" % dateiname

    def anzeigen(self, eintraege):
        ausgabe = ""
        for i, adresse in enumerate(eintraege):
            ausgabe += str(i + 1)
            for key in self.keys:
                ausgabe += " "
                ausgabe += adresse[key]
            ausgabe += "\n"
        return ausgabe            

    def eintragen(self):
        adresse = {}
        anzahl = 0
        for key in self.keys:
            eintrag = raw_input("%s: " % key.capitalize())
            if eintrag:
                adresse[key] = eintrag
                anzahl += 1
            else:
                adresse[key] = ""

        if self.mindestanzahl:
            if anzahl < self.mindestanzahl:
                print "Es müssen mindestens\
 %s Einträge gemacht werden!" % self.mindestanzahl
            else:
                self.eintraege.append(adresse)                

    def bearbeiten(self, nummer):
        adresse = self.eintraege[int(nummer) - 1]
        for key in self.keys:
            if adresse[key]:
                eintrag = raw_input("%s %s neu ? --> " % (key.capitalize(),
                                                          adresse[key]))
            else:
                eintrag = raw_input("%s hinzufügen ? --> " % key.capitalize())
            if eintrag:
                adresse[key] = eintrag                

    def loeschen(self, nummer):
        del(self.eintraege[nummer - 1])        

    def suchen(self):
        suchbegriff = raw_input('Suchen nach?: ')
        self.treffer = []
        for adresse in self.eintraege:
            if suchbegriff in adresse.itervalues():
                self.treffer.append(adresse)
        if self.treffer:
            print self.anzeigen(self.treffer)
        else:
            print 'Leider keinen Eintrag gefunden'        

    def speichern(self, pfad, adressbuch):
        datei = file(self.pfad, "wb")
        pickle.dump(self.eintraege, datei)
        datei.close()
        
        
if __name__ == "__main__":
    adressbuch = Adressbuch("adressbuch.bin",
                            ["name", "vorname", "straße", "nummer", "plz", "ort"],
                             mindestanzahl = 2)
    ergebnis = adressbuch.anzeigen(adressbuch.eintraege)
    print ergebnis
Ganz vielen Dank im voraus,

rolgal
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Ohne mit jetzt alles im Detail angesehen zu haben, aber wie man mit Default-Werten die auf ``self`` zugreifen arbeitet geht so:

Code: Alles auswählen

def anzeigen(self, eintraege=None):
    if not einträge:
        einträge = self.eintraege
Aber Wiederverwertbarkeit außerhalb von Klassen halte ich für völlig sinnlos, weil du dann auch das ``self`` mit Default-Werten belegen müsstest (wenn es nicht an eine Klasse gebunden ist, bekommt es auch kein Implizites ``self`` mit, deswegen). Du musst also quasi unterscheiden ob es an eine Klasse gebunden ist oder nicht. Durch diese Unterscheidung erübrigt sich die Wiederverwertung, weil du so oder so zwei verschiedene Codes schreiben musst - da kannst du auch gleich zwei Funktionen machen, das ist klarer. Was du für die Wiederverwertung machen kannst, sind Sachen die in beiden Funktionen vorkommen in eine separate, von der Klasse unabhängige Funktion auslagern und diese aus beiden Funktionen dann normal aufrufen.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
rolgal_reloaded
User
Beiträge: 312
Registriert: Dienstag 24. Oktober 2006, 19:31

Ich weiss auch nicht, ob ich jetzt alles verstanden habe:-)
Allerdings gings mir nicht, um die Wiederverwendbarkeit ausserhalb der Klassen, es erscheint nur sinnlos, anzeigen self.eintraege übergeben zu muessen. Rein von der Logik. Deinen Vorschlag teste ich gleich mal, das müßte es eh schon lösen,

@edit: jawohl, so funzt es!

danke,

rolgal
BlackJack

Eine Lösung die vielleicht mehr OOP wäre, ist das `Adressbuch` in `Adressliste` umzubenennen, die Initialisierung und das Laden aus einer Datei aufzutrennen und die Suche eine weitere `Adressliste` zurückgeben zu lassen. Dann braucht man nur eine Methode `anzeigen()` die immer das gleiche macht, nämlich sich selbst komplett ausgeben.

Und man könnte auch überlegen welche von den "magischen" Methoden zu implementieren wie `__len__()` und `__iter__()`.
rolgal_reloaded
User
Beiträge: 312
Registriert: Dienstag 24. Oktober 2006, 19:31

Hallo BlackJack,

was du mir vorschlägst kann ich mir jetzt nicht sofort in Code vorstellen :D
Aber was ich mir selber schon gedacht habe:
Würde man alle drei Lösungen durchbesprechen kann man bei der Lösung mit Klasse bald zur Diskussion gelangen vieles, nicht nur den Klassennamen ganz anders, noch allgemeiner zu benennen, denn diese Klasse müsste genausogut für ein Telefonbuch, Musiknotenverzeichnis usw. gebrauchen zu sein.

Es wäre natürlich super, wenn du wieder mal mit Code deine Ideen darlegen könntest:-)

Tu ich mir einfach leichter,

LG

rolgal_reloaded
schlangenbeschwörer
User
Beiträge: 419
Registriert: Sonntag 3. September 2006, 15:11
Wohnort: in den weiten von NRW
Kontaktdaten:

Hi!
Ich weiß ja nicht, wofür du Beispiele möchtest, aber so magische Methoden sind ja eigendlich nur Codeverkürzung:

Code: Alles auswählen

    def __len__(self):
        return len(self.eintraege)

    def __iter__(self):
        for eintrag in self.eintraege:
            yield(eintrag)
Z. 13 - 26 in eine load-Methode zu packen, kannst du wohl ohne Bsp.

Gruß, jj
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

rolgal_reloaded hat geschrieben:es erscheint nur sinnlos, anzeigen self.eintraege übergeben zu muessen. Rein von der Logik. Deinen Vorschlag teste ich gleich mal, das müßte es eh schon lösen,
Dann gib es eben nicht an. Brauchst du auch nicht, denn du übergibst ja schon ``self`` und ``eintraege`` ist ja nur ein Attribut davon, auf das du problemlos zugreifen kannst.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
BlackJack

Nochmal kurz zum Beispiel oben: Beim Speichern werden die Argumente `pfad` und `adressbuch` nicht verwendet. In `suchen()` wird `treffer` unnötigerweise an das Objekt gebunden.

`anzeigen()` zeigt nichts an, sondern liefert eine Zeichenkette. Könnte man also als `__str__()` implementieren und `Adressbuch`-Objekte dann einfach mit ``print`` ausgeben.

Hier das gewünschte Beispiel: http://www.ubuntuusers.de/paste/11081/

Besonders schön ist es natürlich auch nicht, weil eine einzelne Adresse durchaus ein eigenes Objekt sein könnte und Daten und "GUI" vermischt sind.
schlangenbeschwörer
User
Beiträge: 419
Registriert: Sonntag 3. September 2006, 15:11
Wohnort: in den weiten von NRW
Kontaktdaten:

Da es hier ja um Codeverbesserung geht, und dieses Programm gut dafür geeignet ist, da es kurz aber dennoch sinnvoll ist, hätte ich da mal Frage: Man soll ja die Programmierung und die Graphik möglichst komplett auseinanderhalten. Die ´print´ und ´raw_input´-Sachen sind ja eigentlich nur ein GUI-Ersatz, müssten also doch eigentlich auch von der Adressbuch-Klasse getrennt werden, oder? Mich würde das mal interessieren, wie das wirklich sauber geht, da ich, wenn ich Programme mit Gui schreibe, zwar das GUI in eine eigene Klasse packe, die "Inhalts-Klasse" aber genau auf diese abstimme, sodass es fast keine Sinn mehr macht.
Dieses Programm wär doch jetzt mal gut, um es als Grundlage für das ultimative Beispielprogramm für guten Progammierstil zu verbessern, denn es gibt zwar Tutorials für den Inhalt, aber für sowas doch recht selten (habs zumindest noch nicht gesehen).
Was haltet ihr davon, die Klassenversion oben so zu verbessern, dass ein paar Leute sie verwenden können, und, ohne sie zu verändern, ein GUI in dem entsprechenden Toolkit zu schreiben? Ich hab schon überlegt, wie ich das mit Tkinter machen würde, aber da müsste ich die komplette Klasse umbasteln, und die wär dann wieder so, das einer, der ein wx-Beispiel damit machen will, es wieder ändern muss....Zudem käme hierbei noch der Toolkit-Vergleich zustande, da das Programm ja recht gewöhnlich ist, für ein Programm, wofür man ein GUI schreibt.
Ich fänd das echt lehrreich.

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

@alle

Danke nochmal für die interessanten Antworten.
Rein zufällig habe ich sie gesehen, ich bekomme nämlich die halbe Zeit keine Mail, dass ich eine Antwort erhalten habe.

LG

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

BlackJack hat geschrieben:Nochmal kurz zum Beispiel oben: Beim Speichern werden die Argumente `pfad` und `adressbuch` nicht verwendet. In `suchen()` wird `treffer` unnötigerweise an das Objekt gebunden.

`anzeigen()` zeigt nichts an, sondern liefert eine Zeichenkette. Könnte man also als `__str__()` implementieren und `Adressbuch`-Objekte dann einfach mit ``print`` ausgeben.

Hier das gewünschte Beispiel: http://www.ubuntuusers.de/paste/11081/
Das wollte ich ursprünglich auch, hat nicht geklappt, inzwischen glaube ich auf Grund deines Beispieles gesehen zu haben warum.

LG

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

schlangenbeschwörer hat geschrieben:Da es hier ja um Codeverbesserung geht, und dieses Programm gut dafür geeignet ist, da es kurz aber dennoch sinnvoll ist, hätte ich da mal Frage: Man soll ja die Programmierung und die Graphik möglichst komplett auseinanderhalten. Die ´print´ und ´raw_input´-Sachen sind ja eigentlich nur ein GUI-Ersatz, müssten also doch eigentlich auch von der Adressbuch-Klasse getrennt werden, oder? Mich würde das mal interessieren, wie das wirklich sauber geht, da ich, wenn ich Programme mit Gui schreibe, zwar das GUI in eine eigene Klasse packe, die "Inhalts-Klasse" aber genau auf diese abstimme, sodass es fast keine Sinn mehr macht.
Dieses Programm wär doch jetzt mal gut, um es als Grundlage für das ultimative Beispielprogramm für guten Progammierstil zu verbessern, denn es gibt zwar Tutorials für den Inhalt, aber für sowas doch recht selten (habs zumindest noch nicht gesehen).
Was haltet ihr davon, die Klassenversion oben so zu verbessern, dass ein paar Leute sie verwenden können, und, ohne sie zu verändern, ein GUI in dem entsprechenden Toolkit zu schreiben? Ich hab schon überlegt, wie ich das mit Tkinter machen würde, aber da müsste ich die komplette Klasse umbasteln, und die wär dann wieder so, das einer, der ein wx-Beispiel damit machen will, es wieder ändern muss....Zudem käme hierbei noch der Toolkit-Vergleich zustande, da das Programm ja recht gewöhnlich ist, für ein Programm, wofür man ein GUI schreibt.
Ich fänd das echt lehrreich.

Gruß, jj
Gute Idee, bei mir sind da auf Grund der Weiterentwicklung der Lösung 1 - alles global noch viele Dinge drin, die natürlich in weiterer Folge rausgehören. Ich finde es sehr fein, nicht nur für mich, sondern für jeden im Lernprozess, viele Abstufungen bzw. Weiterentwicklungen zu sehen und an ihnen im Detail den Mehrwert zu erkennen.

LG

rolgal_reloaded
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

schlangenbeschwörer hat geschrieben:Da es hier ja um Codeverbesserung geht, und dieses Programm gut dafür geeignet ist, da es kurz aber dennoch sinnvoll ist, hätte ich da mal Frage: Man soll ja die Programmierung und die Graphik möglichst komplett auseinanderhalten. Die ´print´ und ´raw_input´-Sachen sind ja eigentlich nur ein GUI-Ersatz, müssten also doch eigentlich auch von der Adressbuch-Klasse getrennt werden, oder?
Richtig. Die Klasse ist eine API, auf die der Nutzer des Programms keinen Zugriff haben sollte. Die Klasse sollte eine Aufgabe haben und nur eine. Die meisten Klassen sind dazu Daten strukturiert zu kapseln und zu verarbeiten. Die Ausgabe übernimmt ein anderer Code.
schlangenbeschwörer hat geschrieben:Mich würde das mal interessieren, wie das wirklich sauber geht, da ich, wenn ich Programme mit Gui schreibe, zwar das GUI in eine eigene Klasse packe, die "Inhalts-Klasse" aber genau auf diese abstimme, sodass es fast keine Sinn mehr macht.
Dann solltest du die GUI und die Klasse nicht gleichzeitig machen, weil sowas dann zur zu engen Verzahnung zwischen Inhalt (=Daten) und Darstellung (=GUI) kommt, die zu schwer verständlichen Programmen führt.
Eine solche Trennung hat beispielsweise Dookies 7-Segmente-Programm, welches verschiedene UIs hat (auch denn die UI-unabhängige Klasse Properties teilweise sinnlos nutzt. What's On Air ist ebenso UI-unabhängig:
es hat ein Kommandozeileninterface aber auch ein PyGTK-Interface. Eine Zeitlang gab es auch eine Tkinter-UI (die jemand anders geschrieben hat), aber da sie keiner maintaint hat, wurde die rausgenommen.
schlangenbeschwörer hat geschrieben:Dieses Programm wär doch jetzt mal gut, um es als Grundlage für das ultimative Beispielprogramm für guten Progammierstil zu verbessern, denn es gibt zwar Tutorials für den Inhalt, aber für sowas doch recht selten (habs zumindest noch nicht gesehen).
Wäre ok. Aber ich wäre dafür die benötigten Spezifikationen aufzustellen und diese zu implementieren - das geht sicherlich schneller, als rolgal_reloadeds Programm umzuschreiben, denke ich. Zumindest finde ich, dass das schneller geht. Wir können ja auch erstmal Unittests schreiben und dann das Programm implementieren, als Test-first-Ansatz, der von selbst schon eine Art Spezifikation ist.
schlangenbeschwörer hat geschrieben:Ich fänd das echt lehrreich.
Können wir machen. Während man aber eine GUI schreibt, merkt man eben manchmal, dass die API an einigen stellen schlecht ist - dann muss dort nachgebessert werden. Ich glaube aber, dass man sowas merkt, wenn man Unittests schreibt.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
rolgal_reloaded
User
Beiträge: 312
Registriert: Dienstag 24. Oktober 2006, 19:31

BlackJack hat geschrieben:
Hier das gewünschte Beispiel: http://www.ubuntuusers.de/paste/11081/
Vielleicht habe ich mir jetzt zu wenig Zeit gelassen,....wie auch immer, was bringt es die ganzen Methoden wie __len__ zu implementieren. __str__ ist mir klar.
Ich mein die Auswirkung ist schon klar: adressbuch.len(), aber der tatsächliche Nutzen bleibt mir noch verborgen.

Irgendwas seh und/oder check ich da jetzt nicht :oops:

LG

rolgal_reloaded
Antworten