Brauche Hilfe bei einem kleinen Anfängerskript

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.
Antworten
basti33
User
Beiträge: 56
Registriert: Donnerstag 24. August 2006, 15:05

Hallo,

das ist mein erster Post und ich beschäftige mich erst seit ein paar Wochen mit Programmierung bzw. Python .

Ich wollte mir ein kleines Adressbuch basteln. Leider funktioniert das überhaupt nicht, ich bekomme einen Error nach dem anderen. Ich poste meine Arbeit mal hier:

Code: Alles auswählen

#!/usr/bin/python
# -*-coding: utf-8 -*-

# Ein kleines Adressbuch

import cPickle as p

print '''Willkommen in Ihrem Adressbuch!
Wenn Sie eine Adresse hinzufügen möchten, schreiben Sie bitte 'neu', wenn Sie ihre Adressen lesen möchten, schreiben Sie bitte 'lesen'!'''

a = raw_input('Schreiben Sie bitte, was Sie tun möchten: ')
if a == 'neu':
    Adressen.neu(raw_input('Geben Sie den Namen der neuen Person an: '), raw_input('Geben Sie die E-Mail Adresse dieser Person an: '))

elif a=='lese':
    Adressen.lese
    
else:
    print 'Der gewünschte Modus existiert nicht'
class Adressen:
    Adressen='/home/basti/Eigene_Dateien/Programmieren/Programme/Python/Adressbuch/Adressen.sw'
    ab = {}
    def __init__(self, name, email):
        self.name=name
        self.email=email
    
    def neu(self):
        # Fügt eine neue Person hinzu
        Adressen.ab[self.name] = self.email
        f= file(Adressen.Adressen, 'w')
        p.dump(Adressen.ab, f)
        f.close()
    
    def lese(self):
        #Liest die Adressen aus
        f = file(Adressen.Adressen)
        gespeicherteadressen = p.load(f)
        print gespeicherteadressen
Für die meisten hier ist das sicherlich eine Leichtigkeit.
Ich bitte, dass mir hier jemand den richtigen Denkanstos geben kann.

Danke :oops:
Zuletzt geändert von basti33 am Donnerstag 24. August 2006, 15:23, insgesamt 1-mal geändert.
Benutzeravatar
Shana
User
Beiträge: 28
Registriert: Dienstag 22. August 2006, 11:58
Wohnort: Bremen
Kontaktdaten:

Hey,

ich habs jetzt nicht getestet, aber zum Beispiel fehlt dir in Zeile 13 am Ende eine Klammer, die angibt, dass dein Tuple geschlossen wird. Ansonsten funktioniert die nächste Zeile nicht.

Und Klassen werden eigentlich gleich nach den Importen deklariert.
basti33
User
Beiträge: 56
Registriert: Donnerstag 24. August 2006, 15:05

Danke!!
Ich habe es korrigiert. Leider funktioniert es immer noch nicht, ich bekomme jetzt diese Meldung
Das untersuchte Programm erzeugte die Ausnahme unhandled NameError
"name 'Adressen' is not defined"
Datei: /home/basti/Eigene_Dateien/Programmieren/Programme/Python/Adressbuch/Adressbuch.py, Zeile: 13
antimicro
User
Beiträge: 151
Registriert: Sonntag 29. Februar 2004, 16:24

Das Objekt Adressen gibt es ja noch gar nicht, also kannst du davon auch nicht die Methode 'neu' aufrufen.

Am einfachsten kopierst du die ganze Klasse erst einmal vor dem Code und rufst dann einmal die Klasse selbst auf -> Adressen("ich","@")
Zuletzt geändert von antimicro am Donnerstag 24. August 2006, 15:52, insgesamt 1-mal geändert.
greetings
sebi
BlackJack

Das Programm wird zeilenweise abgearbeitet und in Zeile 13 gibt's den Namen `Adressen` noch nicht.

Das Programm hat aber noch mehr Fehler. Zum Beispiel rufst Du die Methoden auf der Klasse auf statt ein Objekt zu erzeugen und sie dann darauf aufzurufen. Die Klassenattribute wären besser Attribute der Objekte und in der `__init__()` übergibst Du einen Namen und eine Mail-Adresse!? Diese Parameter würden bei `Adressen.neu` mehr Sinn machen und die Methode versuchst Du auch so aufzurufen. Nur das sie gar keine Argumente entgegennimmt ausser dem impliziten `self`.
Schildi
User
Beiträge: 30
Registriert: Freitag 19. Mai 2006, 22:23
Kontaktdaten:

Hi!
Das untersuchte Programm erzeugte die Ausnahme unhandled NameError
"name 'Adressen' is not defined"
Datei: /home/basti/Eigene_Dateien/Programmieren/Programme/Python/Adressbuch/Adressbuch.py, Zeile: 13
heißt also, das das Programm den Namen "Adressen" nicht findet oder kennt. Also ich bin jetzt auch nicht so der Freak auf dem gebiet, aber musst du nicht normal erst noch eine Instanz von der Klasse erstellen?

sowas wie

Code: Alles auswählen

add = Adressen('Hans', 'Hans@Hans.de')
sowas müsstest du dann auf jeden fall noch einbaun, wenn mich jetzt nicht alles täuscht :wink:

MfG, Schildi
by: Schildi
basti33
User
Beiträge: 56
Registriert: Donnerstag 24. August 2006, 15:05

Danke für die Antworten.
Ich habe das ganze jetzt mal so abgeändert, und es funktioniert. Das einzigste Problem ist, dass wenn ich eine neue Adresse hinzufüge, die alte gelöscht wird und es dadurch nie mehr als einen Eintrag gibt. Weiß jemand, was man tun kann, damit neue Adressen einfach hinzugefügt werden?

Code: Alles auswählen

#!/usr/bin/python
# -*-coding: utf-8 -*-

# Ein kleines Adressbuch

import cPickle as p

print '''Willkommen in Ihrem Adressbuch!
Wenn Sie eine Adresse hinzufügen möchten, schreiben Sie bitte 'neu', 
wenn Sie ihre Adressen lesen möchten, schreiben Sie bitte 'lesen'
und wenn Sie das Programm beenden möchten, schreiben sie 'ende'!'''

weiter=True
while weiter:
    class Adressen:
        Adressen='/home/basti/Eigene_Dateien/Programmieren/Programme/Python/Adressbuch/Adressen.sw'
        ab = {}
     
        def neu(self, name, email):
            # Fügt eine neue Person hinzu
            Adressen.ab[name] = email
            f= file(Adressen.Adressen, 'w')
            p.dump(Adressen.ab, f)
            f.close()
    
        def lese(self):
            #Liest die Adressen aus
            f = file(Adressen.Adressen)
            gespeicherteadressen = p.load(f)
            print 'Es gibt %d Kontakte im Adressbuch' % len(gespeicherteadressen)
            for name, email in gespeicherteadressen.items():
                print '%s hat die Adresse %s' % (name, email)
    a = raw_input('Schreiben Sie bitte, was Sie tun möchten: ')
    if a == 'neu':
        Adressen().neu(raw_input('Geben Sie den Namen der neuen Person an: '), raw_input('Geben Sie die E-Mail Adresse dieser Person an: '))

    elif a=='lesen':
        Adressen().lese()
    
    elif a=='ende':
        break
    else:
        print 'Der gewünschte Modus existiert nicht'
Malle
User
Beiträge: 29
Registriert: Freitag 11. August 2006, 19:04
Wohnort: Coswig
Kontaktdaten:

Guck dir doch mal das E-mail adressbuch von dem link an. (2. Skript)
http://www.python-forum.de/topic-6377.html
mir hat es auch geholfen.
Michael Janssen
User
Beiträge: 3
Registriert: Freitag 27. Januar 2006, 15:55

Hallo Basti,


es gibt da noch ein paar Dinge in Deinem code, die zwar funktionieren mögen, aber sehr nach Anfängerkreativität aussehen, wenn ich mal so sagen darf.

So sollte die Klassen nicht im while loop definiert werden, sondern einmal am Anfang des Skripts (nach den Imports). Einmal, um evt viele Instanzen davon zu ziehen. So wie man mit einem Bauplan viele Gegenstände bauen kann, ohne den Bauplan zwischendurch kopieren zu müssen.

"Adressen().neu(name, email)" ist insofern eigenartig, weil von der Klasse zwar eine Instanz gezogen wird, aber sie wird nicht zugewiesen und daher gleich wieder verworfen. So behälst Du die Instanz dauerhaft:

adr = Adressen()
adr.neu(name, email)
adr.lese()
(das Instantiieren der Klasse sollte vrmtl auch vorm und nicht innerhalb des while-loops stattfinden).

Bei Dir funktioniert es trotzdem, weil Du zum Speichern der Daten sowieso auf die Festplatte ausweichst. Deine Adressklasse ist eigentlich bloß eine Ummantelung (sacht man so für engl wrapper?) für pickle.dump und pickle.load.

Und das führt dann zu dem Problem, dass alte Adressen überschrieben werden: pickle.dump überschreibt und erweitert nicht etwa. Du must den Inhalt des dumps (der Datei) erst einlesen, das dictionary um die neue Adresse erweitern und dann alle Daten zurückschreiben. Du siehst, es gibt noch was zu tun :-)

Aber nochmal zur Klasse:

Zeile 21: "Adressen.ab[name] = email" sollte idiomatisch "self.ab[name] = email" heißen: Adresse greift auf die Klasse, den Bauplan zu. "self" greift auf die Instanz zu. Zwar darf man ersteres auch und es gibt Fälle wo man genau das machen will - aber falls und solange Du nicht weißt, was überhaupt der Unterschied ist, solltest Du versuchen Deinen code mit self.ab zum Laufen zu bringen.

Tipp: zum Testen von der Konsole würde ich das Programm von interaktiver Eingabe auf Kommandozeile umstellen. So dass Du es zum Testen aufrufst mit:

script.py neu 'ET' 'phone@home.com'
script.py lese

Es spart einfach viel Zeit! Im Skript dann sowas wie:
import sys
modus = sys.argv[1]
name = sys.argv[2]
email = sys.argv[3]


Grüße
Michael
basti33
User
Beiträge: 56
Registriert: Donnerstag 24. August 2006, 15:05

Michael Janssen hat geschrieben: aber sehr nach Anfängerkreativität aussehen,
Ist ja schließlich auch ein Anfängerskript :oops: . Ich habe es jetzt nochmal etwas verändert, aber jetzt ist das Adressbuch grundsätzlich leer, egal ob ich was eingefügt habe oder nicht.

Code: Alles auswählen

#!/usr/bin/python
# -*-coding: utf-8 -*-

# Ein kleines Adressbuch

import cPickle as p

print '''Willkommen in Ihrem Adressbuch!
Wenn Sie eine Adresse hinzufügen möchten, schreiben Sie bitte 'neu', 
wenn Sie ihre Adressen lesen möchten, schreiben Sie bitte 'lesen'
und wenn Sie das Programm beenden möchten, schreiben sie 'ende'!'''


class Adressen:
    Adressen='/home/basti/Eigene_Dateien/Programmieren/Programme/Python/Adressbuch/Adressen.sw'
    ab = {}
    
    def neu(self, name, email):
        # Fügt eine neue Person hinzu
        try:
            f = file(self.Adressen)
            gespeicherteadressen = p.load(f)
            neu=self.ab[name] = email
            f= file(self.Adressen, 'w')
            gespeicherteadressen.append(neu)
            f.close()
        except EOFError:
            self.ab['Test'] = 'test@test.test'
        
    def lese(self):
    #Liest die Adressen aus
        try:
            f = file(self.Adressen)
            gespeicherteadressen = p.load(f)
            print 'Es gibt %d Kontakte im Adressbuch' % len(gespeicherteadressen)
            for name, email in gespeicherteadressen.items():
                print '%s hat die Adresse %s' % (name, email)
        except EOFError:
            print 'Das Adressbuch ist noch leer!'

weiter=True
Adr=Adressen()
while weiter:
    a = raw_input('Schreiben Sie bitte, was Sie tun möchten: ')
    if a == 'neu':
        Adr.neu(raw_input('Geben Sie den Namen der neuen Person an: '), raw_input('Geben Sie die E-Mail Adresse dieser Person an: '))

    elif a=='lesen':
        Adr.lese()
    
    elif a=='ende':
        break
    else:
        print 'Der gewünschte Modus existiert nicht'
Danke
Benutzeravatar
Masaru
User
Beiträge: 425
Registriert: Mittwoch 4. August 2004, 22:17

Hmm ... wird wohl an deiner neu Methode liegen:

Code: Alles auswählen

try:
    f = file(self.Adressen)
    gespeicherteadressen = p.load(f)
    neu=self.ab[name] = email 
    f= file(self.Adressen, 'w')  # <--- du öffnest zwar die file zum schreiben, aber schreibst nichts hinnein
    gespeicherteadressen.append(neu) #<--- was möchtest du in die Datei picklen? Eine Liste, oder ein Dictionary?
    f.close()
except EOFError:
            self.ab['Test'] = 'test@test.test' 
Wenn du die erste von mir kommentierte Zeile mal anschaust, siehst du zwar, dass du ein Dateiobjekt mit dem PFad (richtigerweise auch 'beschreibbar') erzeugst, und es nachher auch wieder schließt ... aber du schreibst nirgendwo dein Adressen-Dictionary in diese hinnein.

Normalerweise schreibt man ja z.B. mit der File-Methode write() in geöffnetes File-Object.

In deinem Falle aber möchtest du mit cPickel arbeiten, welches da etwas aus der Reihe tanzt.

Um ein Objekt (z.B. dein Dictionary) in die Datei zu schreiben, musst du die dump() cPickle-Methode anwenden, z.B. so:

Code: Alles auswählen

def neu(self, name, email):
    try:
        f = file(self.Adressen, 'r')  # für's einlesen
        saved_adressen = cPickel.load(f)
        f.close()
   
        if not isinstance(saved_adressen, dict):
            # muss wohl was falsch gelaufen sein, oder die Datei war leer ;)
            saved_adressen = {} 
        saved_adressen[name] = email

        f = file(self.Adressen, 'w')
        cPickle.dump(saved_adressen, f)  # <--- hier ist der besagte Speicherschritt
        f.close()
    except Exception, msg:
        print "Fehler: %s" % msg
>>Masaru<<
Zuletzt geändert von Masaru am Donnerstag 24. August 2006, 23:06, insgesamt 3-mal geändert.
Benutzeravatar
Masaru
User
Beiträge: 425
Registriert: Mittwoch 4. August 2004, 22:17

Meine Meinung:
... als Anfängerskript/programmieraufgabe finde ich eine Aufgabenstellung sowohl mit Datei Operationen wie auch zudem noch mit Serialisierung ein wenig hart.

Gerade wenn es um "Datei/Verzeichnis"-Zugriffe geht, kann man einige falsch und anderes besser machen, wenn man weiss worauf man achten muss (IO- und OSError beim öffnen/schreiben catchen oder gar mit einplanen an der richtigen Stelle dafür zu sorgen, dass ein finally die geöffnete Datei auch wieder schließt und somit frei gibt, etc. pp).

Wenn du Dich mit Konsolenanwendungen/skripten stärker beschäftigen möchtest, würde ich mich für den Anfang gerade stärker mit folgenden Themen beschäftigen:
- arbeite Dich erst einmal in die Grundmodule und Prinzipien von Python ein
- optparse ist ein Segen von Konsoléros
- Exceptions einzelner Methoden ermitteln und speziell abfangen
- mit der Codierung von Zeichenketten, aber auch Dateien (encodierte StreamWriter/Reader) beschäftigen
- man kann auch mittels dem csv-Modul wunderbar Daten abspeichern (sogar in einer lesbareren Form, im Gegensatz zu pickle ;))
- Config /INI-Dateien erzeugen/lesen stellt auch noch eine weitere Form der Datenspeicherung/Auslesung dar
- wenn du gerne mit Dictionaries arbeites, lerne die Methoden kennen ( has_key() ist eine ideale Methode um schon auf das Vorhandensein von Schlüsseln zu testen, ohnen einen KeyError abfangen zu müssen)
- spezieller Zusicherungen machen, oder auf das tatsächliche Vorhandensein von Objekten, deren Typ und Inhalt prüfen (z.B. mittels isinstance())
-etc.

:) ... wer hätte je gedacht, dass doch eine namentlich als Skriptsprache abgefrühstückte Sprache so komplex sein kann.
basti33
User
Beiträge: 56
Registriert: Donnerstag 24. August 2006, 15:05

Danke für die Antwort.

Ich habe mir jetzt mal dein 'neu' genommen und damit meines ersetzt. Das "Programm" gibt immer, wenn ich etwas hinzugefügt habe
Fehler:
aus und beim Abfragen ist das Adressbuch nach wie vor leer. Was ich auch nicht versteh, ist, warum

Code: Alles auswählen

        if not isinstance(saved_adressen, dict):
            # muss wohl was falsch gelaufen sein, oder die Datei war leer ;)
            saved_adressen = {} 
        saved_adressen[name] = email
hier in Zeile 4 um zwei Tabs eingerückt wird :?:

Ich bedanke mich vielmals für die Hilfe
Benutzeravatar
Masaru
User
Beiträge: 425
Registriert: Mittwoch 4. August 2004, 22:17

basti33 hat geschrieben:... Was ich auch nicht versteh, ist, warum

Code: Alles auswählen

if not isinstance(saved_adressen, dict):
            # muss wohl was falsch gelaufen sein, oder die Datei war leer ;)
            saved_adressen = {} 
        saved_adressen[name] = email
hier in Zeile 4 um zwei Tabs eingerückt wird :?:
*g* Python benutzt eine 4er Space Blockeinrückung. Ich hatte da also nichts um 2 Tabs (man sollte eh unter Python mit Leerzeichen, und weniger mit Tabs arbeiten; die meisten Editoren bieten auch von sich aus oder via Plugin an, Tabs->in->Whitespaces umzuwandeln) sondern den Block zu dem if-Statement mit 4 Leerzeichen eingerückt.

Ich prüfe in dem if-Block ab, ob es sich bei dem Objekt saved_adressen auch wirklich um ein Objekt des Types 'Dictionary' handelt, da ansonsten die Dictionary-Methoden gehörig einen um die Ohren fliegen können, falls es sich nicht um eins handelt.

Code: Alles auswählen

if not isinstance(saved_adressen, dict):
    # dann tue das, was hier um 4 Zeichen eingerückt drunter steht
    . 
    .
    .
Es hällt ja schließlich niemanden davon ab *g*, in deine Adressdatei eventuell auch mal ein anders Objekt, als ein Dictionary zu dumpen ;).
Benutzeravatar
Masaru
User
Beiträge: 425
Registriert: Mittwoch 4. August 2004, 22:17

So, und um nun nochmal zu deinem Adress Manager zu kommen, habe ich hiermal ein bisschen Code, den du bei dir testweise austauschen kannst (betrifft die 'Imports' sowie deine Klasse 'Adressen'; der Rest bleibt wie gehabt!)

Code: Alles auswählen

import cPickle
import os
import sys


class Adressen(object):

    def __init__(self):
        """Initialisierung"""
        self.__adress_datei= '/home/basti/Eigene_Dateien/Programmieren/Programme/Python/Adressbuch/Adressen.sw'
        if not os.path.exists(self.__adress_datei):
            print "Erstelle Adressdatei: %s ..." % self.__adress_datei,
            try:
                f = open(self.__adress_datei, 'w')
                f.close()
                print "erfolgt."
            except (IOError, OSError), msg:
                print "fehlgeschlagen! (%s)" % msg
                sys.exit(1)

        if os.path.isdir(self.__adress_datei):
            print "FEHLER: Kann Adressdatei nicht erstellen, es existiert " \
                  "ein gleichnamiges Verzeichnis bereits: %s" % self.__adress_datei
            sys.exit(2)

    def __hole_adressen(self):
        """Da an zwei Stellen die Adresen aus der Adressdatei geladen wird, 
        war ich mal so frei und habe diese Funktionalität in eine separate
        Methode ausgelagert ... dieser hier"""
        saved_adressen = {}
        try:
            f = file(self.__adress_datei, 'r')
            try:
                saved_adressen = cPickle.load(f)
            finally:
                f.close()
        except EOFError:
            pass
        except (IOError, OSError), msg:
            print "FEHLER: Nicht lesbare Adressdatei: %s" % msg
            return # <--- Methode beenden

        if not isinstance(saved_adressen, dict):
            return {}
        return saved_adressen


    def neu(self, name, email):
        """Erstellungsfunktion"""
        saved_adressen = self.__hole_adressen()
        saved_adressen[name] = email
        try:
            f = file(self.__adress_datei, 'w')
            try:
                cPickle.dump(saved_adressen, f)
            finally:
                f.close()
        except (EOFError, IOError, OSError), msg:
            print "FEHLER: Konnte Daten nicht in Adressdatei schreiben (%s)" % msg

    def lese(self):
        """Lesefunktion"""
        saved_adressen = self.__hole_adressen()
        if not saved_adressen or not isinstance(saved_adressen, dict):
            print 'Das Adressbuch ist noch leer!'
            return # <--- Methode beenden

        print 'Es gibt %d Kontakte im Adressbuch' % len(saved_adressen)
        for name, email in saved_adressen.items():
            print '%s hat die Adresse %s' % (name, email)
An einigen Stellen habe ich das Dateispezifische Exception-Handling erweitert, sowie mit einer Funktionalitätenauslagerung und der 'eigentlich' in Python-Klassen recht nützlichen __init__ Klassenmethode erweitert.

Sollte für erste Testläufe und Verständlichkeit von Python helfen denke ich.

Was mir bei Dir noch aufgefallen ist, ist dass du gerne mal auslässt eine zuvor geöffnete Datei (egal mit welchem Attribut 'r', 'w', 'a' etc. ... denn das ist völlig egal *g*) auch wieder mit close() zu schließen.

>>Masaru<<
Antworten