Optimierung eines Telefonbuchs

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.
Seeker
User
Beiträge: 70
Registriert: Mittwoch 16. September 2009, 19:52

Hi!

Ich habe jetzt mein erstes "eigenes Programm" geschrieben. Wie der Name schon sagt ist es ein Telefonbuch, wo man Personen mit ihren Infos speichern, aufrufen, löschen etc. kann.

Das Programm funktioniert soweit, doch ich habe ein paar Fragen.
a) Ich wollte ermöglichen, dass nicht jede Person alle Einträge besitzt (z.B. jemand hat kein Handy), und habe dies mit all diesen if-Statements gelöst. Gibt es für solche Fälle eine elegantere Lösung?
Ihr dürft auch gern sagen, dass der ganze Programm-Ansatz falsch ist =P.

b) Wenn man bei Input() ein "x" eintippt, möchte ich, dass durch das h=False der while-loop beendet wird, das neue Dict gedumped und das File geschlossen wird. Allerdings scheint das Programm immer noch zu meinen, h = True. Weshalb?
Wenn ich gleich h = False als command eingebe, kommt ein IOError zurück: "p.dump(p_b, f) IOError: [Errno 9] Bad file descriptor" ? =/

c) Bei der Adresse möchte ich, dass man als input Strasse\nStadt eingeben kann und dies dann auf zwei Zeilen angezeigt wird... ich verstehe nicht ganz weshalb dies hier nicht geschieht. Meine erste Programmversion benutzte eine Liste statt einem Dictionary, und bei dieser hat es die Adresse korrekt angezeigt.

Der Code:

Code: Alles auswählen

# Filename: Phonebook.py

import cPickle as p

class Entry:
    '''Entry in the Phonebook'''
    def __init__(self, name, t_number = "", m_number = "", job="", address="", e_address=""):
        self.name   = name
        self.t      = t_number
        self.m      = m_number
        self.job    = job
        self.address=address
        self.e_add  = e_address
        print "(Initialized %s's entry)" % self.name

    def tell(self):
        '''Tell details of entry'''
        print 'Name: %s \n' % self.name
        if self.t != "":
            print 'Tel. Number: %s' % self.t
        if self.m != "":
            print 'Mob. Number: %s \n' % self.m
        if self.job != "":
            print 'Occupation: %s \n' % self.job
        if self.address != "":
            print 'Address: %s \n' % self.address
        if self.e_add != "":
            print 'Email: %s \n' % self.e_add

class Interactive:
    '''Manages all input from user'''
    def Input(self):
        new = raw_input ("For new entry, press n;\nTo delete entry, press d;\n\
To show details of entry, enter name;\n\
For entire Phonebook, press e;\nTo exit, press x:\n")
        if new == "n":
            name = raw_input("Name:")
            t_n = raw_input("Tel. number:")
            m_n = raw_input("Mob. number:")
            job = raw_input("Occupation:")
            address = raw_input("Address:")
            e_address = raw_input("Email:")
            temp = Entry(name, t_n, m_n, job, address, e_address)
            p_b[name] = temp
        if new == "d":
            name = raw_input("Which entry would you like to delete?:")
            del p_b[name]            
        if p_b.has_key(new):
            p_b[new].tell()
        if new == "e":
            for i in p_b.keys():
                p_b[i].tell()
                print "*********************************************"
        if new == "x":
            h = False


phonebookfile = "phonebook.data"
i = Interactive()
try:
    f = open(phonebookfile)
    p_b = p.load(f)
except IOError:
    Phonebook = {}
    p_b = Phonebook
    f = open(phonebookfile, "w")
h=True
while h:
    a = raw_input("Press Enter to continue...or enter new command:\n")
    if a == "":
        i.Input()
    else:
        exec(a)
p.dump(p_b, f)
f.close()
Danke im Voraus =)

lg Seeker
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Seeker hat geschrieben: a) Ich wollte ermöglichen, dass nicht jede Person alle Einträge besitzt (z.B. jemand hat kein Handy), und habe dies mit all diesen if-Statements gelöst. Gibt es für solche Fälle eine elegantere Lösung?
Ihr dürft auch gern sagen, dass der ganze Programm-Ansatz falsch ist =P.
Wozu eine Klasse? Versuche das einfach erst einmal mit einem Dictionary zu lösen. Dazu studiere einfach mal den Abschnitt im Tutorial dazu :-) Damit kann man auch relativ elegant nur die EInträge anzeigen, die vorhanden sind.
b) Wenn man bei Input() ein "x" eintippt, möchte ich, dass durch das
h=False der while-loop beendet wird, das neue Dict gedumped und das File geschlossen wird. Allerdings scheint das Programm immer noch zu meinen, h = True. Weshalb?
Wenn ich gleich h = False als command eingebe, kommt ein IOError zurück: "p.dump(p_b, f) IOError: [Errno 9] Bad file descriptor" ? =/
Du bindest an die lokale Variable h den Wert False. Dann bindest Du in der (sinnlosen Klasse Interactive) ein Objektattribut an False. Das sind aber zwei unterschiedliche Variablen.

Probiere mal folgendes aus:

Code: Alles auswählen

class Foo(object):
    def __init__(self):
        self.bar = False

bar = True
f = Foo()
print bar, f.bar
c) Bei der Adresse möchte ich, dass man als input Strasse\nStadt eingeben kann und dies dann auf zwei Zeilen angezeigt wird... ich verstehe nicht ganz weshalb dies hier nicht geschieht. Meine erste Programmversion benutzte eine Liste statt einem Dictionary, und bei dieser hat es die Adresse korrekt angezeigt.
Das verstehe ich leider nicht.

Generell solltest Du Dir PEP8 mal durchlesen:
http://python.org/dev/peps/pep-0008/
Deine Namen im Programm sind nicht wirklich konform damit.

Womit hast Du denn Python bisher gelernt?
Seeker
User
Beiträge: 70
Registriert: Mittwoch 16. September 2009, 19:52

Schonmal danke =).
Ich bin mir bewusst, dass die "Interactive"-Klasse sinnlos ist. Ich wollte einfach eine Klasse benutzen. :roll:
Den Rest schau ich mir noch genauer an =)

Gelernt hab ichs bis jetzt mit "A Byte of Python"
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Seeker hat geschrieben:Schonmal danke =).
Ich bin mir bewusst, dass die "Interactive"-Klasse sinnlos ist. Ich wollte einfach eine Klasse benutzen. :roll:
hehe... solltest Du aber besser nicht, solange Du nicht an die Grenzen der einfachen Datenstrukturen stößt; das ist schon viel Stoff, vor allem, wenn man mit Python das Programmieren anfängt.
Gelernt hab ichs bis jetzt mit "A Byte of Python"
Das ist schon mal eine gute Wahl. Ich würde Dir dennoch zusätzlich das offizielle Tutorial (aus der Doku) empfehlen. Kann nie schaden :-)
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Hallo.

Ich habe noch ein paar Hinweise für dich gesammelt:

Gewöhne dir auch an richtige Namen zu verwenden. "t" und "m" sind vollkommen nichtssagend. Du solltest daran denken, dass du dir dein Programm vielleicht in drei Monaten wieder anschaust. Dann wirst du mit solchen Namen sicherlich nichts mehr verstehen, obwohl du es selber geschrieben hast ;-) "cPickle" solltest du sicher auch nicht als "p" importieren, sondern besser als "pickle"

Die ganze ungarische Notation solltest du auch sein lassen, sie macht bei Python überhaupt keinen Sinn. Es kommt auf die Schnittstelle eins Objekts an, nicht auf den Typ (Duck Typing).

Dann solltest du keinen Code auf Modul-Ebene haben, sondern diesen durch ein

Code: Alles auswählen

if __name__ == "__main__":
    main()
schützen. Andernfalls kannst du deine Module nirgends importieren.

Ein

Code: Alles auswählen

print "*********************"
kannst du ganz einfach durch ein

Code: Alles auswählen

print "*"*42
ersetzen.

Es gibt "elif".

Da du offensichtlich noch mit Python in einer Version < 3.0 arbeitest, solltest du von "object" erben.

Konstanten sind eine ganz tolle Erfindung ;-)
Das Leben ist wie ein Tennisball.
Seeker
User
Beiträge: 70
Registriert: Mittwoch 16. September 2009, 19:52

Wozu eine Klasse? Versuche das einfach erst einmal mit einem Dictionary zu lösen. Dazu studiere einfach mal den Abschnitt im Tutorial dazu Smile Damit kann man auch relativ elegant nur die EInträge anzeigen, die vorhanden sind.
Ich habe es durchgelesen, allerdings nichts gefunden, wahrscheinlich am falschen Ort geschaut. Und ich benutze ja ein Dictionary.
Du bindest an die lokale Variable h den Wert False. Dann bindest Du in der (sinnlosen Klasse Interactive) ein Objektattribut an False. Das sind aber zwei unterschiedliche Variablen.
Stimmt, klar. Gibt es eine andere Lösung als global? (oder einfach zwei Auswege zu haben wie jetzt)
Das verstehe ich leider nicht.

Generell solltest Du Dir PEP8 mal durchlesen:
http://python.org/dev/peps/pep-0008/
Deine Namen im Programm sind nicht wirklich konform damit.
Das kann gut sein... ich habe die Dinge mehr oder weniger einfach so genannt wie ich sie mir merken kann. Kannst du mir ein Beispiel geben?

Wenn ich als Adresseneintrag: 'Backalley 34\n23541 Ghosttown' eingebe, möchte ich, dass es auf zwei Zeilen angezeigt wird.
Es zeigt es aber so an:

Code: Alles auswählen

Name: Mr. Smith 

Tel. Number: 234 342 34 34
Mob. Number: 456 345 23 23 

Occupation: Agent 

Address: Backalley 34\n23542 Ghosttown 

Press Enter to continue...or enter new command:
Ich habe trotzdem das Programm nochmals überarbeitet, beide Klassen entnommen und die ganzen if-Statements überarbeitet. Ich weiss allerdings nicht, wie ich diese ganz los werden kann.

Code:

Code: Alles auswählen

# Filename: Phonebook.py

import cPickle as pickle

def tell(x):
    print 'Name: %s \n' % x[0]
    if x[1] != "":
            print 'Tel. Number: %s' % x[1]
    if x[2] != "":
            print 'Mob. Number: %s \n' % x[2]
    if x[3] != "":
            print 'Occupation: %s \n' % x[3]
    if x[4] != "":
            print 'Address: %s \n' % x[4]
    if x[5] != "":
            print 'Email: %s \n' % x[5]

def Input():
    new = raw_input ("For new entry, press n;\nTo delete entry, press d;\n\
To show details of entry, enter name;\n\
For entire Phonebook, press e;\nTo exit, press x:\n")
    if new == "n":
        name = raw_input("Name:")
        tel_nr = raw_input("Tel. number:")
        mob_nr = raw_input("Mob. number:")
        job = raw_input("Occupation:")
        address = raw_input("Address:")
        e_address = raw_input("Email:")
        temp = [name, tel_nr, mob_nr, job, address, e_address]
        pb[name] = temp
    if new == "d":
        name = raw_input("Which entry would you like to delete?:")
        del pb[name]            
    if pb.has_key(new):
        tell(pb[new])
    if new == "e":
        for i in pb.keys():
            tell(pb[i])
            print "*"*45
    if new == "x":
        pickle.dump(pb, f)
        f.close()
        exit()

phonebookfile = "phonebook.data"

try:
    f = open(phonebookfile)
    pb = pickle.load(f)
except EOFError:
    Phonebook = {}
    pb = Phonebook
    f = open(phonebookfile, "w")
    
h=True
while h:
    a = raw_input("Press Enter to continue...or enter new command:\n")
    if a == "":
        Input()
    else:
        exec(a)
pickle.dump(pb, f)
f.close()
Immer, wenn ich p.dump einsetze, bekomme ich diese Fehlermeldung.. was stimmt nicht? =/

Code: Alles auswählen

Input
    p.dump(pb, f)
IOError: [Errno 9] Bad file descriptor
*EDIT* Ich war am schreiben, als die neue Antwort kam. Werde dies gleich bearbeiten ;).
Zuletzt geändert von Seeker am Mittwoch 23. September 2009, 14:43, insgesamt 1-mal geändert.
Seeker
User
Beiträge: 70
Registriert: Mittwoch 16. September 2009, 19:52

Also... ich habe nun die Namen etwas verständlicher gemacht und "*"*45 genutzt ;) (Habe obrigen Quelltext erneuert).

Wie genau würde mir elif hier helfen?
Die ganze ungarische Notation solltest du auch sein lassen, sie macht bei Python überhaupt keinen Sinn. Es kommt auf die Schnittstelle eins Objekts an, nicht auf den Typ (Duck Typing).

Dann solltest du keinen Code auf Modul-Ebene haben, sondern diesen durch ein

if __name__ == "__main__":
main()

schützen. Andernfalls kannst du deine Module nirgends importieren.
Was genau meinst du mit der ungarischen Notation? ^^

Das __name__ == '__main__': ... überprüft doch, ob das Modul direkt ausgeführt wird oder importiert wurde...? Was meinst du mit "Code schützen"? Sry für die banalen Fragen, bin nur leicht verwirrt ;).

*EDIT*: Mittlerweile habe ich einen neuen Versuch gestartet: Jede Person is ein key im dictionary, wobei die value für eine Person wieder ein dictionary ist. Damit lassen sich all diese i statements umgehen.
Momentan habe ich allerdings noch das Problem, dass die Resultate bei Abfrage nicht mehr schön dargestellt werden (einerseits da sie im Dict nicht geordnet bleiben, andererseits weil ich überall eine Zwischenzeile habe, was vorher nicht der Fall war)

Code: Alles auswählen

#!/Lib/site-packages/Programmieren
# Filename: Phonebook.py

import cPickle as pickle

def tell(person_info):
    for detail in person_info:
        if person_info[detail]!= "":
            print "%s %s \n" %(detail, person_info[detail])

def Input():
    action = raw_input ("For new entry, press n;\nTo delete entry, press d;\n\
To show details of entry, enter name;\n\
For entire Phonebook, press e;\nTo exit, press x:\n")
    if action == "n":
        name = raw_input("Name:")
        tel_nr = raw_input("Tel. number:")
        mob_nr = raw_input("Mob. number:")
        job = raw_input("Occupation:")
        address = raw_input("Address:")
        e_address = raw_input("Email:")
        person = {'Name:': name, 'Tel. Number:':tel_nr, 'Mob. Number':mob_nr,\
                'Occupation:': job, 'Address:':address, 'Email:':e_address}
        pb[name] = person
        print "-Person successfully added to Phonebook-\n"
    if action == "d":
        name = raw_input("Which entry would you like to delete?:")
        del pb[name]            
    if pb.has_key(action):
        tell(pb[action])
    if action == "e":
        for i in pb.keys():
            tell(pb[i])
            print "*"*45
    if action == "x":
        pickle.dump(pb, f)
        f.close()

phonebookfile = "phonebook.data"

try:
    f = open(phonebookfile, "w")
    pb = pickle.load(f)
except IOError:
    Phonebook = {}
    pb = Phonebook
    f = open(phonebookfile, "w")
    
h=True
while h:
    a = raw_input("Press Enter to continue...or enter new command:\n")
    if a == "":
        Input()
    else:
        exec(a)
pickle.dump(pb, f)
f.close()
print "Done"
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Na also - es wird doch langsam :-)

Ok, also ich würde jetzt mal diese unsägliche Input()-Funktion entrümpeln. (Input() ist z.B. eine Funktion, die laut PEP8 klein geschriben werden muss - input() ist aber keine gute Alternative, da das eine Built-in Funktion ist.)

Ich würde eine Kernfunktion schreiben, in der es einen unendlichen loop gibt, in dem einfach die Steuerkommandos abgefragt werden. Je nach Eingabe verzwigt die Funktion dann in andere (laden, speichern, neu eingeben usw).

Ich würde mir überlegen, ob ich wirklich den Nachnamen als Key in einem dict nehmen würde. So kann es ja keine zwei Schmidts geben ;-)

Evtl. würde ich also eine Liste nehmen, in der die einzelnen Personen dicts drin liegen, oder eben eine "künstlichd" ID einführen.

Und dann vor allem schnell mal EyDu's Anmerkung mit dem "Code auf Modulebene" umsetzen. Das geht ja echt fix.
Benutzeravatar
jbs
User
Beiträge: 953
Registriert: Mittwoch 24. Juni 2009, 13:13
Wohnort: Postdam

Es ist ganz gut den Quellcode in einem pastebin (http://paste.pocoo.org/) auszulagern, um das Forum nicht voll zu"spammen"
[url=http://wiki.python-forum.de/PEP%208%20%28%C3%9Cbersetzung%29]PEP 8[/url] - Quak!
[url=http://tutorial.pocoo.org/index.html]Tutorial in Deutsch[/url]
Seeker
User
Beiträge: 70
Registriert: Mittwoch 16. September 2009, 19:52

Danke, werd ich tun =).

Ich hab mich irgendwie in meinem Code-Durcheinander verloren, hab das Gefühl, dass alles immer länger und fehlerhafter wird ^^.

Das mit der Modulebene ist mir nur halbwegs klar, aber ich habs mal ein wenig anders aufgebaut. Sagt mir doch, ob ich auf dem richtigen Weg bin ;).
Mein Problem ist nun, dass nichts funktioniert, weil gewisse Variablen ja global sein sollen, ich aber möglichst kein global-Statement benutzen möchte. Wie löst man das Problem am Besten, ohne wieder in meine Modulebene zu fallen? =/
Muss ich dafür aus main() eine Klasse machen, dort meine Variabeln pb = Phonebook (die überall gebraucht wird) definieren und dann alle anderen Module dieser Klasse unterordnen? =/

Wäre um ein paar Tipps in diese Richtung sehr froh :D:

Der neue Code:
http://paste.pocoo.org/show/141172/

Was die Speicherung der Namensdaten etc. habe ich noch nichts verändert. Es zeigt sie immer noch quer durcheinander an (d.h. momentan überhaupt nicht ^^).
Und nein, da ich den ganzen Namen (Vor- und Nachnamen) als Key habe, können durchaus zwei Leute den gleichen Nachnamen haben.
Zuletzt geändert von Seeker am Donnerstag 24. September 2009, 09:33, insgesamt 1-mal geändert.
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Hallo.

Zu deinem "global"-Problem: die Lösung sind Rückgabewerte und Parameter.

Zu deinem jetzigen Code:

Die main-Funktion würde man in Zeile 67 erwarten.

'if a != "":' kannst du auchals "if a:" schreiben.

"exec" ist eine wirklich böse Sache, werde das los. Damit kannst du BELIEBIGEN Python-Code ausführen. Damit kannst du dir wunderschöne Sicherheitslücken bauen ;-) Außerdem ist exec keine Funktion, du solltest also die umschließenden Klammern sparen.

Funktionsnamen werden "in_diesem_format_geschrieben". Großbuchstaben sollten da nicht drin vorkommen (PEP 8). Zum Beispiel "organzize_files" oder "display_all".

Die while-Schleife würde man so nicht schreiben, eher so:

Code: Alles auswählen

def Interaction():
    while True: #HIER IST DIE ERSTE AENDERUNG
        action = raw_input ("...")
        if action == "n":
            new()
        elif action == "d":
            name = raw_input("Which entry would you like to delete?:")
            del pb[name]
        elif action == "e":
            displayall()
        elif pb.has_key(action):
            tell(pb[action])
        elif action == "x":
            break #HIER IST DIE ZWEITE AENDERUNG
        else:
            print "Command not recognized"
Auch solltest du vor dem ersten if ein "action = action.lower()" machen, damit wird eine Eingabe in Kleinbuchstaben gewandelt. Dann kann man zum Beenden auch "X" eingeben. Das die Aktion "d" Fehler werfen kann sollte dir auch klar werden. Versuche mal einen Eintrag zu löschen, der NICHT enthalten ist. Dann suchst du nach Exceptions und try/except.

In Zeile 43 ist der Backslash am Ende überflüssig. Bei geklammerten Ausdrücken wird dieser nicht benötigt.

In Zeile 49 kannst du dir das ".keys()" sparen. Es wird automatisch über die Schlüssel iteriert. Besser: benutze ".values()", dann sparst du dir das "pb". Noch besser: benutzer ".itervalues()". Wenn du Schlüssel und Wert brauchst: "for key, value in spam.iteritems():". (siehe Funktion "tell")

In Zeile 66 sollte es "Phonebook()" heißen.
Das Leben ist wie ein Tennisball.
Seeker
User
Beiträge: 70
Registriert: Mittwoch 16. September 2009, 19:52

Super, vielen Dank =).
Ich werde mich ein bisschen mehr in Rückgabewerte und Parameter einlesen und alle genannten Dinge versuchen einzubauen... und morgen hoffentlich eine wieder funktionierende Version zum Vorschein bringen.

lg Seeker
Seeker
User
Beiträge: 70
Registriert: Mittwoch 16. September 2009, 19:52

So... nächste Version =).

Neben den anderen kleinen Veränderungen habe ich mich mit Parametern umgeschlagen. Ich hätte interaction() und pickle.dump(pb, f) lieber im main() gehabt (statt als 'unterfunktion' vom organize_file), aber das ist ein Detail und anders hab ichs nicht hingekriegt.

Nur ein Punkt ist mir etwas unklar:
In Zeile 66 sollte es "Phonebook()" heißen.
Weshalb? Phonebook ist doch einfach der name des Dict, oder nicht? :?

Nun habe ich aber noch zwei Probleme, die ich nicht lösen konnte.
a) pickle.dump(..) funktioniert nicht (vielleicht, weil pb nur im interaction verändert wurde nur dann zurück im organize_file wieder in den Ursprungszustand zurückkehrt?). Wie kann ich dies machen, ohne pickle.dump() als unterfunktion von interaction() einzusetzen?

b) Bei meiner ersten Version des Programms hatte ich ja meine Personendetails in einer Liste, und habe sie mithilfe von vielen if-statements ausgegeben. All dies bin ich jetzt ja mit dem for key, value in... und dem Dictionary ungangen. Allerdings bleiben die Werte im Dictionary ja nicht geordnet.
Wie kann ich einstellen, dass zuerst Name, dann Tel. nr, Mob. nr. usw. angegeben wird, und dass z.B. zwischen allen eine Leerzeile kommt, ausser zwischen den Telefonnummern?
Dazu kommt, dass meine Adresse immer noch wie ein raw_string behandelt wird, wobei sie bei Eingabe von z:B. Dorfstrasse\n23454 Hinterhausen auf zwei Zeilen ausgegeben werden sollte.

http://paste.pocoo.org/show/141258/

Danke für die Mühe! =)
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

- nee, nee, es sollte phonebook statt Phonebook heißen. Ich weiß nicht, worauf sich EyDu da bezogen hat, aber im aktuellen Quellcode ist der Einwand unberechtigt.

- Für die Ausgabe ist das Stichwort "Templating". Damit könntest Du ein Template einer Ausgabe erstellen und nur noch die Daten einfügen. Alternativ musst Du eben die Keys der Reihe nach abarbeiten.

Code: Alles auswählen

# manuell Reihenfolge ableiten
order = ["name", "adresse", "telefon"]
for key in order:
    print "{0}: {1}".format(key, peron_info[key])
- in organize_file() musst Du fürs Lesen auch ein "r" als Parameter mitgeben... Ich würde diese Funktion aber splitten und eine fürs Schreiben und eine fürs Laden basteln.
Generell würde ich eine Dateioperation so formulieren:

Code: Alles auswählen

try:
    # "r" oder "w" je nach Aufgabe
    with open("foo.txt", "r") as data:
        # Daten irgend wie auslesen
        bar = data.read()
except IOError, e:
    # Fehler ausgeben, zur Not per sys.exit() raus o.ä.
    print e
else:
    # mache noch irgendwas mehr mit 'bar'
    pass
- vergiss "del", benutze lieber dict.pop(<key>), also in Zeile 16 pb.pop(name)

- interaction() ist imho kein guter Name. Wie wäre es mit manage_menu()? Desweiteren kann man sich diese if-elif-else Zeilen sparen, indem man eine ein Dict verwendet. Beispiel:

Code: Alles auswählen

def foo(*args):
    print "bin in foo()"
    print "habe folgende Parameter:"
    print args


def bar(*args):
    print "bin in bar()"


def dispatch(choice="f", *args):
    # wir mappen Zeichen auf Funktionsnamen
    mapping = {
        "f": foo,
        "b": bar
    }
    try:
        # es wird die Funktion aufgerufen, die wir anahnd von choice gewählt haben
        mapping[choice](args)
    except KeyError, e:
        print "Sorry, Kommando '{0}' unbekannt".format(choice)
Generell würde ich an der Reiehnfolge im Quellcode einiges ändern. Grunsätzlich würde ich vom grundlegenden, "atomaren" hinunter zum immer komplexeren die Funktionen aufschreiben. Also die main()-Funktion logischer Weise ganz unten, ganz oben jedoch eine sehr elemetare Funktion, wie das "Laden" oder so etwas, aber auch die Ausgabe. Sprich alles, in dem Du keine weitere Funktion aus dem Modul selber aufrufst. Darunter dann die etwas komplexeren Dinge, wie z.B. display_all(), die ja eben die irgend wo darüber liegende tell()-Funktion aufruft (wieso eigentlich tell und nicht display_one()?). Uns so weiter, bis eben über der main vermutlich das handle_menu() oder so auftaucht.

So, das wars erst einmal :-)
Seeker
User
Beiträge: 70
Registriert: Mittwoch 16. September 2009, 19:52

Danke =).
Ich habe heute allerdings viele Stunden mit diesen Codes gespielt und das Internet durchstöbert... und bekomme z.T einfach keine funktionierende Syntax raus :(.
Zuerst aber mal allgemein:
in organize_file() musst Du fürs Lesen auch ein "r" als Parameter mitgeben... Ich würde diese Funktion aber splitten und eine fürs Schreiben und eine fürs Laden basteln.
Wie darf ich das verstehen?
Ich habe ja zwei Wege:
1) Die Datei existiert schon, dann wird sie geöffnet und das Dictionary durch cPickle geladen.
2) Die Datei existiert nicht, dann wird sie geschrieben, geöffnet und ein leeres Dictionary wird erstellt.

Bei beiden ist die Datei am Schluss also offen, weshalb pickle.dump() und dann close() kein Problem sein sollte. Früher ging es auch, aber seit ich den Code aus der Modul-Ebene genommen habe, wird phonebook.data nicht aktualisiert. (es kommt aber keine Fehlermeldung)

Jetzt spezifisch:
Das mit dem Template habe ich schlussendlich so halbwegs hingekriegt, aber dispatch will einfach nicht klappen =/.
Das Problem ist wieder "pb", welches ich in die Funktion mitnehmen muss und dann weitergeben sollte.... und keine Syntax scheint zu gehen.

Hier mein Testprogramm:

Code: Alles auswählen

def apple(hello):
    print "aas", hello

def bee(hello):
    print "bee", hello

def cedar(hello):
    print "cedar"

def dispatch(choice = "f", hello):
    mapping = {"a": apple,
               "b": bee,
               "c": cedar}
    try:
        mapping[choice](hello)
    except KeyError:
        print "Sorry, Kommando '{0}' unbekannt".format(choice)
        
def main():
    f = raw_input("was eingeben")
    hello = 20
    dispatch(f, hello)

if __name__ == "__main__":
    main()
Fehler:

"non-default argument follows default argument (line 10)"

Ich werde mir noch überlegen, das ganze vielleicht doch wieder zurückzuwandeln, wie es früher war. d.h. ich würde die Personen statt in einem Dictionary in einer Liste speichern und durch verschiedene if's das Print-Statement machen.

Bei Interesse:


http://paste.pocoo.org/show/141291/
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Hallo.

Zunächst zu dem "Phonebook": Das kommt davon, wenn man Variablen mit einem Großbuchstaben beginnt ;-) Ich bin davon ausgegangen, dass es eine Klasse ist und habe keine Zeile weiter nach oben geschaut.

Zu deinem Dateiproblem: In Zeile 8 öffnest du die Datei nur zum Lesen, dann kannst du in Zeile 17 nicht einfach etwas hineinschreiben. Du musst sie also zum Lesen und Schreiben öffnen. Schau dir mal die Parameter zu "open" an.

Dein Syntax-Fehler (poste doch bitte nächstes Mal den ganzen Traceback) ist einfach zu lösen und sagt eigentlich schon die Meldung: Du kannst einer Funktion keine Parameter so übergeben, dass zunächst einem Parameter ein Standardwert zugewiesen wird und dem dahinter nicht. Wie soll das eindeutig ausgewertet werden. Was bedeutet dispatch(1)? Heißt dass, dass choice=1 ist und hello fehlt, oder dass hello=1 und choice="f"?
Das Leben ist wie ein Tennisball.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Seeker hat geschrieben: Wie darf ich das verstehen?
Ich habe ja zwei Wege:
1) Die Datei existiert schon, dann wird sie geöffnet und das Dictionary durch cPickle geladen.
Ja und da hast Du aber open() nicht gesagt, dass Du Lesen ("r") möchtest, sondern Schreiben ("w")!
2) Die Datei existiert nicht, dann wird sie geschrieben, geöffnet und ein leeres Dictionary wird erstellt.
Ich habe jetzt gesehen, dass Du die Datei offen lässt und aus der Funktion dann in interactive wechselst! Das ist Mist! Mach das anders. Am Schluss läuft es immer darauf hinaus, dass Änderungen als eine neue Datei gespeichert werden müssen. Schreib also eine Funktion, die genau das abdeckt. Den Rumpf dazu hatte ich Dir ja schon gepostet. Als Parameter braucht die dann eben einen Dateinamen und das Telefonbuch.

Dann schreib eine Funktion, die eine Datei öffnet und ein Telefonbuch aus den daten erstellt. (Ebenfalls mein "Template" dazu nutzen). Hier musst Du nur evtl. ein "leeres" Telefonbuch zurückgeben, wenn die Datei eben nicht existiert.

Diese beiden Funktionen musst Du nun aus Deinem Menü heraus aufrufbar machen. Während der Benutzer arbeitet sind alle Dateien immer geschlossen!
Jetzt spezifisch:
Das mit dem Template habe ich schlussendlich so halbwegs hingekriegt, aber dispatch will einfach nicht klappen =/.
Sorry, war mein Fehler. Die Reihenfolge zwischen default und non-default-Argumenten spielt in Python natürlich eine Rolle:

Code: Alles auswählen

# richtig
def foo(bar, choice="f")
    pass

# falsch
def foo(choice="f", bar)
   pass
Also lass das default-Argument eben weg - man ruft dispatch ja eh mit einem Wert auf.

Code: Alles auswählen

# durch das * kannst Du beliebig viele non-default Parameter übergeben!
def dispatch(choice, *args):
    mapping = {"a": apple,
               "b": bee,
               "c": cedar}
    try:
        mapping[choice](args)
    except KeyError:
        print "Sorry, Kommando '{0}' unbekannt".format(choice)
Ich werde mir noch überlegen, das ganze vielleicht doch wieder zurückzuwandeln, wie es früher war. d.h. ich würde die Personen statt in einem Dictionary in einer Liste speichern und durch verschiedene if's das Print-Statement machen.
Wozu der Rückfall zu so einer schlechten Lösung?
Seeker
User
Beiträge: 70
Registriert: Mittwoch 16. September 2009, 19:52

Danke für die Antworten =)!
In Zeile 8 öffnest du die Datei nur zum Lesen, dann kannst du in Zeile 17 nicht einfach etwas hineinschreiben.
Ja und da hast Du aber open() nicht gesagt, dass Du Lesen ("r") möchtest, sondern Schreiben ("w")!
:D ...aber ist mein Fehler.
Ich hätte noch betonen sollen, dass ich dein Template nicht ignoriert hatte, sondern schon eingebaut hatte, nur nicht ganz funktionsfähig ^^.
Aber der andere Hinweis hat mich auf die richtige Spur gebracht.
Die Datei wird neuerdings gleich nach dem öffnen/erstellen und ggf. dem pickle.load wieder geschlossen. Beim schliessen des Programms wird die Datei dann wieder im "w" Mode geöffnet und das Telefonbuch durch pickle.dump abgelegt.
Sorry, war mein Fehler. Die Reihenfolge zwischen default und non-default-Argumenten spielt in Python natürlich eine Rolle
omg... das hätt ich eigentlich auch selber gewusst :/... habs ja x mal gelesen ^^. Irgendwie ... keine Ahnung, Wand vor den Augen ;).
Wozu der Rückfall zu so einer schlechten Lösung?
Das Telefonbuch funktioniert mittlerweilen super. Allerdings wollte ich eigentlich die Ausgabe so haben (so war die Ausgabe mit der Liste):

Code: Alles auswählen

Name: Mr. Smith 

Tel. Number: 234 234 23 23 
Mob. Number: 875 453 43 85

Occupation: Agent 

Address: 
Fleestreet 10
95748 Witchtown 

Email: thecakeisalie@portal.com
Aber es gibt sie mir so an:

Code: Alles auswählen

Name: Mr. Smith 

Tel. Number: 234 234 23 23

Mob. Number: 875 453 43 85

Occupation: Agent 

Address: Fleestreet 10\n95748 Witchtown 

Email: thecakeisalie@portal.com 
Neu:
http://paste.pocoo.org/show/141297/
Dav1d
User
Beiträge: 1437
Registriert: Donnerstag 30. Juli 2009, 12:03
Kontaktdaten:

ich hab mir den Code zwar nicht angeschaut, aber es dürfet an dem liegen:

Code: Alles auswählen

>>> def a():
	print 'a'
	print 'b'
	print 'c'

	
>>> a
<function a at 0x011BF1F0>
>>> a()
a
b
c
>>> def a():
	print 'a',
	print 'b',
	print 'c',

	
>>> a
<function a at 0x011BF8B0>
>>> a()
a b c
die 3 Prints kann man auch z.B. mit

Code: Alles auswählen

abc = ['a', 'b', 'c']
for i in abc:
    print i
ersetzten

wenn meine Vermutung stimmt muss du hinter die Print-Anweisung eine Komma setzten

Code: Alles auswählen

print 'Hallo',
mfg[/code]
the more they change the more they stay the same
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Also glaub mir doch mal: Deine organize_file() Funktion ist Mist! Zum einen brauchst Du kein "finally:" mehr durch das with-Statement, zum anderen sind da viele sinnlose Abläufe drin.

Mal das ganze kommentiert:

Code: Alles auswählen

def organize_file():
    # würde ich als Parameter übergeben, ggf. eben als default-Parameter
    phonebookfile = "phonebook.data"
    try:
        with open(phonebookfile) as f:
            pb = pickle.load(f)
            f.close()
            # das ist Käse! Man würde von der Funktion manage_file nicht erwarten, dass
            # diese eine andere (dazu noch mit endlos loop)
            # aufruft
            manage_menu(pb)
    except IOError:
        # wieso willst Du mit dem Dateinamen erneut arbeiten
        # der hat ja wohl Probleme verursacht!
        # zudem vollkommen überflüssig - wozu eine leere Datei schreiben?
        # außerdem eine neue Fehlerquelle! Was passiert,
        # wenn open() hier eine Exception auslöst?
        f = open(phonebookfile, "w")
        f.close()
        # vollkommen umständlich die 3 Zeilen!
        # manage_menu(dict()) tut das selbe
        phonebook = {}
        pb = phonebook
        manage_menu(pb)
    # durch das with überflüssig - in finally sollte Aufräumarbeiten stattfinden,
    # wie z.B. das Schließen von Dateien
    # außerdem eine neue Fehlerquelle, wie oben!
    finally:
        f = open(phonebookfile, "w")
        pickle.dump(pb, f)
        f.close()
Also noch einmal zusammenfassend: Dein Konzeot ist hier umständlich, unsauber und gefährlich! Versuche es doch so zu lösen, wie ich es Dir davor schon beschrieben haben. Eine Funktion fürs Öffnen und Einlesen, eine separate fürs Speichern. Beides sollte im Menü anwählbar sein; also einmal ein Befehl zum Öffnen und einmal einer zum Speichern. Wird Dir viel einfacher fallen, glaub mir!

Wieso das mit der Ausgabe nicht so klappt weiß ich aus dem Stehgreif auch nicht. Muss damit zusammenhängen, dass das Steuerzeichen "\n" wohl irgend wie escaped ist...
Antworten