Aufbau von Klassen

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.
zikzak
User
Beiträge: 21
Registriert: Sonntag 22. September 2013, 07:28
Wohnort: Sipbachzell, Österreich

Hallo geschätzte an Alle,


dank diesem Forum bin ich schon ein ganzes Stück weiter im Sinne "Verständnis",
ein paar Sachen sind mir aber dennoch nicht klar - hier ein noch sinnfreies Beispiel:

Code: Alles auswählen

class Adresse():
    def __init__(self,bezeichnung="", strasse ="", plz="", ort="", land=""):
        self._bezeichnung=bezeichnung
        self._strasse=strasse
        self._plz=plz
        self._ort=ort
        self._land=land
dann eine weitere Klasse:

Code: Alles auswählen

class Kontakt():
    Identifikation = 0
    def __init__(self, name, vorname="", anrede="", titel="", mobil=""):
        
        self._identifikation=Kontakt.Identifikation
        self._anrede=anrede
        self._name = name
        self._vorname=vorname
        self._titel=titel
        self._mobil=mobil
       
Da der Kontakt ja mehrere Adressen haben kann, reicht es nach meinem Verständnis nicht,
einfach von der Klasse Adresse zu erben. Wie macht man das jetzt ?
Einbau einer Liste von Adresse als Items ?
Zuerst eine Containerklasse von Adresse bauen und die dann in der Klasse Kontakte einbinden ?
z.B. Klasse die von List erbt und zusätzlich dem Wohnort/Standort einen Namen gibt - "Ferienhaus", "Hauptsitz" ,...

Gemacht habe ich testweise das Ganze auch schon als Klasse die von dict erbt, wobei z.B.

Code: Alles auswählen

{"_name":"Müller", "_vorname":"Josef" } 
die Adressen (Mehrzahl) als Liste in den Values gespeichert habe.

Wie macht das der Profi ?
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

zikzak hat geschrieben:Da der Kontakt ja mehrere Adressen haben kann, reicht es nach meinem Verständnis nicht,
einfach von der Klasse Adresse zu erben. Wie macht man das jetzt ?
Einbau einer Liste von Adresse als Items ?
Exakt. Modelliere das so, wie du es im Text beschriebenhast: "Ein Kontakt kann mehrere Adressen haben". Also ist die Liste von Adressen ein Attribut von Kontakt.

Ob du etwas anderes willst als eine Liste, wie z.B. ein Dictionary, hängt davon ab, wie du auf die Daten zugreifen möchtest. Solltest du eine eigene Containerklasse mit speziellen Methoden brauchen, dann setze diese ein. Den genauen Anwendungsfall kennst nur du. Fakt ist aber, dass die Adressen von der Anforderung her auf jeden Fall ein Attribut von Kontakt sein müssen.
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Zu deinem Code noch ein paar Anmerkungen: Die führenden Unterstriche bei den ganzen Attributen kannst du in diesem Fall sicher sein lassen. Es handelt sich wohl eher nicht um Implementierungsdetails, sondern es werden nur Daten gehalten. Bei den Standardwerten der Parameter solltest du von einem leeren String auf None übergehen. Dann sieht man sofort, dass keine Angabe gemacht wurde und kann das von leeren Eingaben unterscheiden. Und dann stellt sich mir noch die Frage, was ``Identifikation`` darstellen soll. Das sieht hier irgendwie gefährlich aus.
Das Leben ist wie ein Tennisball.
BlackJack

@zikzak: Warum sind die Attribute denn alles Implementierungsdetails? Das sind ja letztendlich eher Datenklassen, also bräuchte man für jedes Attribut einen trivialen Getter was nicht wirklich „pythonisch” ist, sondern einfach nur unnötige Schreibarbeit.

Was willst Du mit `Kontakt.Identifikation` bewirken?

Von den eingebauten Containerklassen würde ich nicht erben solange man nicht auch tatsächlich die ganzen Methoden sinnvoll verwenden kann, welche die Klassen schon anbieten. Sonst hat man ganz schnell Objekte die Methoden haben mit denen man die Werte ”kaputt” machen kann.
zikzak
User
Beiträge: 21
Registriert: Sonntag 22. September 2013, 07:28
Wohnort: Sipbachzell, Österreich

Zunächst mal ein großes DANKE für die vielen Antworten in so kurzer Zeit !
Sobald ich mal das Niveau habe, konstruktives zu schreiben, werde ich mich daran erinnern.

Identifikation meine eindeutige Nummer, die ich eventuell noch brauchen kann.
Der Klassenzähler (mir großem I) Identifikation wird am Ende von __init__ um 1 erhöht.

Das mit dem "None" wäre eine weitere Frage von mir gewesen :lol:
Bin ja noch in der Erlern-Phase.

Code: Alles auswählen

class Kontakt(Adressen):
    Identifikation = 0
    def __init__(self, name,firmenname="",sap=0,ag=False, we=False,anrede="",
                 vorname="", titel="", mobil="", emails=[], lastchanged="noch nie",
                 geburtstag=None,adressen=None,hobbies=None,
                 verkäufergruppe="",uid="",zahlungsbedingungen="",
                 text_zahlungsbedingungen="",relations=[],gekauft_letztes_jahr=False):
        """ Init der Oberklasse """
        Adressen.__init__(self,adressen)
        
        self._identifikation=Kontakt.Identifikation
        self._anrede=anrede
        self._name = name
        self._firmenname=firmenname
        self._vorname=vorname
        self._titel=titel
        self._mobil=mobil
        self._emails=emails
        self._lastchanged = lastchanged
        self._geburtstag=geburtstag
        self._hobbies=hobbies
        """ Hier der Block, den nur Einträge aus der kunden.csv bzw. cove.csv haben   """
        self._sap=sap
        self._ag=ag
        self._we=we
        self._verkäufergruppe=verkäufergruppe
        self._uid=uid
        self._zahlungsbedingungen=zahlungsbedingungen
        self._text_zahlungsbedingungen=text_zahlungsbedingungen
        self._relations=relations 
        self._gekauft_letztes_jahr=gekauft_letztes_jahr
        Kontakt.Identifikation += 1    
        
    def __getitem__(self,key):
        if key==0: return self._identifikation
        elif key==1 and self._anrede: return self._anrede
        elif key==2 and self._titel: return self._titel
        elif key==3 and self._vorname: return self._vorname
        elif key==4 and self._name: return self._name
        elif key==5 and self._adressen: return self._adressen[0]._strasse
        elif key==6 and self._adressen: return self._adressen[0]._ort
        elif key==7 and self._adressen: return self._adressen[0]._land
        elif key==8 and self._adressen: return self._adressen[0]._plz
        elif key==9 and self._adressen: return self._adressen[0]._tel
        elif key==10 and self._mobil: return self._mobil
        elif key==11 and self._emails: return self._emails
        elif key==12 and self._sap: return self._sap
        elif key==13 and self._verkäufergruppe: return self._verkäufergruppe
        elif key==14 and self._lastchanged: return self._lastchanged 
        elif key==15 and self._geburtstag: return self._geburtstag
        elif key==16 and self._hobbies: return self._hobbies
        
    def __setitem__(self,key,item):
        if key==0: self._identifikation = item
        elif key==1: self._anrede = item
        elif key==2: self._titel = item
        elif key==3: self._vorname = item
        elif key==4: self._name = item
        elif key==5: self._adressen[0]._strasse = item
        elif key==6: self._adressen[0]._ort = item
        elif key==7: self._adressen[0]._land = item
        elif key==8: self._adressen[0]._plz = item
        elif key==9: self._adressen[0]._tel = item
        elif key==10: self._mobil = item
        elif key==11: self._emails = item
        elif key==12: self._sap = item
        elif key==13: self._verkäufergruppe = item
        elif key==14: self._lastchanged = item
        elif key==15: self._geburtstag = item
        elif key==16: self._hobbies = item

    def __repr__(self):
        pass
Die Methoden __setitem__ und __getitem__ bitte nicht ernst nehmen. Ich habe darüber gelesen
und wollte es einfach ausprobieren.
Die ._ habe ich verwendet, weil sich der Text dann leichter liest - ein Problem wären ja nur __
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Jetzt hast du es genau so gemacht, wie du es nicht machen solltest. Verbung ist ein IST-EIN-Beziehung. Wenn du also über Verberung nachdenkst, dann muss diese Bedingung erfüllt sein. In deinem Fall musst du dir die Frage stellen: Ist ein Kontakt eine Adresse? Ganz offensichtlich ist es das nicht, also ist Vererbung der falsche Weg. Nun stellt sich natürlich die Frage, wie die Beziehung von Adresse und Kontakt aussieht. Ganz einfach: Ein Kontakt HAT-EINE Adresse (Komposition). Das heißt, ein Kontakt besitzt ein Attribut Adresse. Oder in diesem Fall: ein Kontakt HAT-MEHRERE Adressen, also besitzt Kontakt eine Liste von Adressen. Diese Liste übergibst du einfach der __init__-Funktion von Kontakt:

Code: Alles auswählen

class Adresse(object):
    def __init__(self, adressen ,...):
        self.adressen = adressen
Im Allgemeinen gilt: Komposition ist Vererbung vorzuziehen. BlackJack hat ja schon in etwa geschrieben warum. Du handelst dir damit jede Menge Nebenschauplätze ein, welche nicht sein müssen. Oft braucht man nur eine handvoll der Funktionalität oder einige Methoden sind komplett unsinnig für den neuen Datentyp. Auch hat man häufig das Problem, dass die Ergebnisse der geerbten Funktion den Typ der Elternklasse haben. Das will man natürlich meistens nicht. Also Vorsicht bei Vererbung!

Auch solltest du deine __getitem__-Spielereien lassen (oder sie uns zumindest nicht zeigen, wenn sie nur zum testen sind), die sind kompletter Unsinn. Das ganze könnte man theoretisch noch weiter zusammenfassen:

Code: Alles auswählen

def __getitem__(self, key):
    value = [self.identifikation, anrede, ...][key]
    if value:
        return value
Der Schlüssel kann übrigens auch ein String sein, dann wird das ganze etwas aussagekräftiger. Aber wie gesagt, in diesem Beispiel völlig unnütz.

Von ``Identifikation`` solltest du auch erstmal Abstand halten. Zunächst ist es eine schlechte Idee Dinge einbauen zu wollen, weil man sie "vielleicht" einmal braucht. Und wenn du die Nummer in diesem Fall doch einmal brauchen solltest, dann ist dein Ansatz falsch. Dann brauchst du entweder eine Datenbank, ein ORM oder eine neue Struktur.
zikzak hat geschrieben:Die ._ habe ich verwendet, weil sich der Text dann leichter liest - ein Problem wären ja nur __
Abgesehen davon, dass es sich mit Unterstrichen nur schlechter lesen lässt. führende Unterstriche haben in Python eine Bedeutung: "fass mich nicht an, außer du weißt genau was du tust". Wenn du sie also nur aus Gründen der "Lesbarkeit" verwendest, dann lass es. Du implizierst damit sonst eine Bedeutung, welche es gar nicht gibt.
Das Leben ist wie ein Tennisball.
zikzak
User
Beiträge: 21
Registriert: Sonntag 22. September 2013, 07:28
Wohnort: Sipbachzell, Österreich

Danke EyDu,

das mit der Erklärung HAT MEHRERE habe ich jetzt endlich verstanden, ist eine 1 zu * Assoziation.

_ merke ich mir und ändere ich.
das mit dem __getitem__ auch, wobei mir

Code: Alles auswählen

def __getitem__(self, key):
    value = [self.identifikation, anrede, ...][key]
    if value:
        return value
etwas zu hoch ist - das schnalle ich auf die Schnelle nicht.
BlackJack

@zikzak: Die Listen als Default-Werte sind ein Fehler. Defaultwerte werden nicht bei jedem Aufruf einer Funktion oder Methode neu erstellt, sondern nur *einmal* wenn die Funktion oder Methode erstellt wird. Dass heisst *alle* Kontakte teilen sich diese Listen und wenn man die verwenden und etwas hinzufügt, dann fügt man das dem *einen* Defaultwert hinzu und das ist dann bei allen aktuellen und zukünftig erstellten Objekten sichtbar, die diesen Wert verwenden.

`hobbies` klingt nach Mehrzahl, da sollte man einen leeren Container an das Attribut binden und nicht `None`. Dann muss man nicht immer die Sonderbehandlung machen und auf `None` testen.

Die `__repr__()`-Methode auf diese Weise kalt zu stellen macht Debug-Ausgaben schwieriger. Statt einer Ausnahme ist die `object.__repr__()` vorzuziehen.
zikzak
User
Beiträge: 21
Registriert: Sonntag 22. September 2013, 07:28
Wohnort: Sipbachzell, Österreich

OK, folgendes umgesetzt:

1. Blödsinn mit __getitem__ weg
2. statt "" jetzt None
3. _ weg
4. Adresse ist eine Klasse und Adressen einfach nur eine Liste davon, die übergeben wird
5. Identifikation ebenfalls weg

Code: Alles auswählen

class Adresse(object):
    def __init__(self,bezeichnung=None, strasse =None, plz=None, ort=None, land=None,tür="",
                 treppe=None, tel=None,fax=None,postfach=None):
        
        self.bezeichnung=bezeichnung
        self.strasse=strasse
        self.plz=plz
        self.ort=ort
        self.land=land
        self.tür=tür
        self.treppe=treppe
        self.tel=tel
        self.fax=fax
        self.postfach=postfach

Code: Alles auswählen

class Kontakt(object):
    Counter = 0
    def __init__(self, name,firmenname=None,sap=0,ag=False, we=False,anrede=None,
                 vorname=None, titel=None, mobil=None, emails=[], lastchanged=None,
                 geburtstag=None,adressen=[],hobbies=[],
                 verkäufergruppe=None,uid=None,zahlungsbedingungen=None,
                 text_zahlungsbedingungen=None,gekauft_letztes_jahr=False):
       
        self.anrede=anrede
        self.name = name
        self.firmenname=firmenname
        self.vorname=vorname
        self.titel=titel
        self.mobil=mobil
        self.emails=emails
        self.lastchanged = lastchanged
        self.geburtstag=geburtstag
        self.hobbies=hobbies
        self.adressen = adressen
        """ Hier der Block, den nur Einträge aus der kunden.csv bzw. cove.csv haben   """
        self.sap=sap
        self.ag=ag
        self.we=we
        self.verkäufergruppe=verkäufergruppe
        self.uid=uid
        self.zahlungsbedingungen=zahlungsbedingungen
        self.text_zahlungsbedingungen=text_zahlungsbedingungen
        self.gekauft_letztes_jahr=gekauft_letztes_jahr
        Kontakt.Counter += 1    
    
    def add_adresse (self, adresse):
        self.adressen.append(adresse)
so besser ?
BlackJack

@zikzak: `Identifikation` ist ja einfach nur umbenannt. Und der Fehler mit den Listen als Default-Argumente ist noch drin.
zikzak
User
Beiträge: 21
Registriert: Sonntag 22. September 2013, 07:28
Wohnort: Sipbachzell, Österreich

Counter zählt mir die Anzahl der Kontakte, wo ist da der Fehler ?
Vorhin habe ich in der Instanz eine id gespeichert, dachte auf das war die Kritik bezogen ?!

Unter http://www.python-kurs.eu/python3_klassen.php
Statische Member wird das genau so erklärt.

Das mit den Listen als Defaultwerte checke ich nicht.
Sollen die auch mit None initialisiert werden ?
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

zikzak hat geschrieben:Das mit den Listen als Defaultwerte checke ich nicht.
Sollen die auch mit None initialisiert werden ?
Die Liste wird nicht bei jedem Aufruf neu initialisiert.

Code: Alles auswählen

class Thing(object):
    def __init__(self, data=[]):
        self.data = data

first = Thing()
second = Thing()
first.data.append('Hello')
print(second.data)
Hier wirst du feststellen, dass "Hello" ausgegeben wird, da beide Instanzen auf der gleichen Liste arbeiten.

Die Lösung mit None sähe dann wie folgt aus

Code: Alles auswählen

class Thing(object):
    def __init__(self, data=None):
        if data is None:
            self.data = []
        else:
            self.data = data
Oder kürzer

Code: Alles auswählen

    def __init__(self, data=None):
        self.data = [] if data is None else data
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

zikzak hat geschrieben:Counter zählt mir die Anzahl der Kontakte, wo ist da der Fehler ?
Und wenn du ein Objekt kopierst passt auf einmal der Zähler nicht mehr.
zikzak hat geschrieben:Unter http://www.python-kurs.eu/python3_klassen.php
Statische Member wird das genau so erklärt.
Dort werden Instanzvariablen je nach Anzahl der Unterstriche auch als public, protected und private bezeichnet. Erklärt ist es im Text besser, aber diese Semantik ist schlicht falsch.
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

zikzak hat geschrieben:Counter zählt mir die Anzahl der Kontakte, wo ist da der Fehler ?
Vorhin habe ich in der Instanz eine id gespeichert, dachte auf das war die Kritik bezogen ?!
Einfach ganz weglassen, das brauchst du nicht. Warum willst du wissen wie viele Kontakte jemals erzeugt wurden. Das zählst du nämlich und nicht die Anzahl der existierenden Kontakte. Wenn du die Anzahl haben willst, dann schaust die wie viele Elemente in der Liste der Kontakte drinstecken. Fertig ;-)
Lass lieber die Finger von dem Tutorial, das steht recht viel mist drin. Halte dich an das offizielle Tutorial aus der Dokumentation. Wenn du Englisch nicht verstehst, dann gibt es das auch auf deutsch. Ansonsten wird hier häufig auch Learn Python the Hard Way empfohlen.
Das Leben ist wie ein Tennisball.
BlackJack

@zikzak: Ähm, die ID ist doch genau das selbe wie der Zähler jetzt. Was genau zählst Du denn da? Und *warum*?

Das Beispiel von dem verlinkten Tutorial ist falsch. `__del__()` funktioniert nicht als deterministischer Destruktor so wie es dort verwendet wird. Es wird von der Sprachdefinition weder garantiert wann die Methode aufgerufen wird, noch ob sie überhaupt jemals aufgerufen wird. Das blosse vorhandensein der Methode kann unter Umständen dafür sorgen, dass die Speicherverwaltung das Objekt niemals freigibt und damit auch niemals die `__del__()`-Methode aufgerufen wird, obwohl das Objekt im Programm nicht mehr erreichbar ist. `__del__()` sollte man nur implementieren wenn man ganz genau verstanden hat wie die Speicherverwaltung funktioniert und dann nur zum verwalten von *externen* Ressourcen die der Laufzeitumgebung von Python nicht bekannt sind und von denen der Anwender einer Bibliothek erwartet dass sie automatisch verwaltet werden. Beispiel dafür wären Datentypen die per `ctypes` Speicherstukturen von einer externen Bibliothek erhalten haben von denen Python keine Ahnung hat, die externe C-Bibliothek aber davon ausgeht, dass der Benutzer den Speicher wieder freigibt wenn er ihn nicht mehr braucht.

Im Openbook von Galileo ist das gleiche Beispiel. Hier ist auch ein Beispiel beschrieben bei dem ein unerwartetes Ergebnis beim Zähler heraus kommt: http://blog.marc.rintsch.de/2012/11/18/ ... destruktor

Ein globaler Zähler für einen Datentyp macht auch in den wenigsten Fällen Sinn, wie das mit den meisten globalen Daten ist. Du wirst die erzeugten Kontakte ja in irgend einem Container-Objekt speichern. Und von dem kannst Du dann auch leicht abfragen wie viele Kontakte enthalten sind. Im Programm können durchaus mehr existieren. Entweder welche an die man nicht gedacht hat, zum Beispiel weil sie in einem lokalen Namensraum einer Aufrufhierarchie der letzten Ausnahme noch gebunden sind, oder auch absichtlich in weiteren Kontaktlisten.
Dami123
User
Beiträge: 225
Registriert: Samstag 23. Februar 2013, 13:01

Wenn du einer Klasse Variablen zuweist, müssen diese nicht unbedingt vordefiniert sein, sprich du kannst "None" weglassen.
Zudem könnte man die ganzen Informationen in einer list, tuple oder dict übergeben, statt jeden Wert.

Eindeutig würde sich ein dictionary mehr lohnen. Anstatt jeden Wert eine Variable zuzuweisen, kannst du aus ner dict den jeweils benötigten Wert auslesen.

Code: Alles auswählen

class Adresse(object):
    def __init__(self, Benutzerdaten):
        self.Benutzerdaten = Benutzerdaten
        print self.Benutzerdaten["Fax"]


if "__main__"==__name__:
    Benutzerdaten = {'Fax': '66', 'Land': 'oiewru', 'Postfach': '1', 'Treppe': 'ert56', 'Bezeichnung': 'sdgg', 
                    'Strasse': 'sdfsdfsf', 'Tuer': '345345', 'Ort': 'sadfsdfsf', 'Telefon': 'sdfsdf', 'Postleitzahl': 'fgjk'}
    Adresse(Benutzerdaten)
zikzak
User
Beiträge: 21
Registriert: Sonntag 22. September 2013, 07:28
Wohnort: Sipbachzell, Österreich

Danke /me,

Code: Alles auswählen

class Thing(object):
    def __init__(self, data=[]):
        self.data = data
 
first = Thing()
second = Thing()
first.data.append('Hello')
print(second.data)
durch deine Erklärung sind mir die Schuppen aus den Augen gefallen !
Eigentlich könnte man ja selbst darauf kommen, das tut ein Anfänger leider nicht immer.
Um so erschreckender, da in keinem meiner 4 Bücher auf diesen Sachverhalt eingegangen wird.

Das mit dem Counter ist jetzt auch klar.
Die Klassenvariablen scheinen nur Sinn zu machen, wenn jedes Objekt die Daten lesend benötigt.

@EyDU, danke für den Tip, das Geld werde ich investieren.


@Damie123:
Einen ähnlichen Ansatz habe ich bereits getestet, allerdings erbte ich von dict.
Hier spricht nichts gegen Vererbung da eine 1:1-Beziehung vorliegt, oder sehe ich da wieder etwas falsch ?

In dem Buch "Python von Kopf bis Fuß" wird das mit einer Liste erklärt, mit dem Argument, dass
man dann alle Operationen der Liste verwenden kann.
Mittlerweile bin ich etwas tiefer drin und sehe darin keinen wirklichen Vorteil.
Wenn ich einen Standard-Container (so wie Du es vorschlägst) übergebe, kann ich genauso alle Operationen verwenden.

Code: Alles auswählen

class Adresse(object):
    def __init__(self, Benutzerdaten):
        self.Benutzerdaten = Benutzerdaten
        print self.Benutzerdaten["Fax"]
zu

Code: Alles auswählen

class Adresse(dict):
    def __init__(self,name, adressdaten):
        dict.__init__({})
        self.name=name
        self.update(adressdaten)
        
        

a = Adresse("Alex", {"FAX":"94059405"})
print (a.name)
print (a["FAX"])
Was ist da klüger ?
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

zikzak hat geschrieben:@EyDU, danke für den Tip, das Geld werde ich investieren.
Auf der Seite ist das vollständige Buch als HTML-Version enthalten. Du kannst also recht entspannt die ersten Kapitel erstmal so durchgehen und erst bei Gefallen zulegen.
zikzak hat geschrieben:Einen ähnlichen Ansatz habe ich bereits getestet, allerdings erbte ich von dict.
Hier spricht nichts gegen Vererbung da eine 1:1-Beziehung vorliegt, oder sehe ich da wieder etwas falsch ?
Wenn du in die Versuchung kommst von dict zu erben, dann lass es ;-) Prinzipiell ist jedes Objekt modellierbar als Dictionary (und so wird es intern in Python auch gemacht), du köntest also jedes Mal davon erben. Außerdem hast du dann wieder die oben genannten Probleme. Die copy-Methode liefert zum Beispiel ein Dictionary zurück und nicht deinen Datentypen, damit kannst du dich schön in die Nesseln setzen.

Du solltest dir gut überlegen, ob du die Daten als Dictionary übergibst oder in Form von vielen einzelnen Parametern. Willst du wirklich alle möglichen Felder zulassen? Wie sieht es mit Defaultwerten aus, die musst du irgendwo behandeln. Gibt es Eigenschaften, welche auf jeden Fall übergeben werden müssen?

Bei der Gelegenheit könnntest du dir noch Keyword-Parameter anschauen, damit könntest du das erstellen eines extra Wörterbuchst umgehen. Das natürlich seine ganz eigenen Nachteile.
Das Leben ist wie ein Tennisball.
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

@zikzak: Bei deinen Beispielen schmeißt du wild mit (gefühlten) tausenden von Attributen für die Klassen um dich. Das halte ich für unrealistisch. Man hat vielleicht 5-6 verschiedene Sektionen, die man in einem solchen Datensatz führen möchte und der Rest der Daten verteilt sich auf diverse andere Klassen.

Sinnvolle Sektionen für eine Adresse wären: Straße, Hausnummer, Adresszusatz, PLZ, Ort, Land. Wenn man mag, dann kann man "Adresszusatz" als Klasse implementieren, wo dann nochmal Flur, Aufgang, Treppe usw denkbar sind. Wobei ich das in den meisten Anwendungsfällen für unnötig halte und das eher als String mit beliebigem Inhalt laufen lassen würde.

Bei den Pflichtangaben muss man ziemlich aufpassen. Falls es international sein soll, dann würde ich wahrscheinlich nur "Ort" zur Pflichtangabe machen. Denn es gibt allein nur in Deutschland weitaus mehr ungewöhnliche Adressangaben, als man im ersten Moment vielleicht denkt. Dann wird es schön blöd, wenn man nicht weiterkommt, weil die Hausnummer fehlt (und als verpflichtend definiert wurde), aber die Adresse dummerweise überhaupt keine Hausnummer hat.

Achja, und die Telefon- und Faxnummer gehören ganz bestimmt nicht zu einer Adressangabe. Das sind nämlich Kontaktangaben. Ein solcher Kontakt würde dann seinerseits eine Instanz von `Adresse` als Attribut führen. Das wurde ja glaub ich auch schon hier im Thread angesprochen.

Insgesamt würde ich sagen: Wenn man bezüglich der Gliederung / Modellierung sauber arbeitet - also keine "artfremden" Angaben innerhalb eines Datencontainers miteinander vermischt - dann kommt man meiner Meinung nach durchaus auf das oben erwähnte Maximum von 5-6 verschiedenen Angaben innerhalb eines Containers. Und das sieht dann natürlich auch im Code des Programms insgesamt etwas schöner aus. ;)
zikzak
User
Beiträge: 21
Registriert: Sonntag 22. September 2013, 07:28
Wohnort: Sipbachzell, Österreich

Danke snafu für deine Korrekturen !
@EyDu : werde ich mir heute Abend noch reinziehen.

Ich habe das ganze nochmal geschrieben, noch nicht als Dict (zwecks Training) und bin wieder auf ein paar Probleme gestossen.
Um eine Ausgabe zu erhalten, habe ich die __str__ formuliert.
Eng wurde es dann bei der Klasse Adressen. Hier musste ich schummeln, weil mir nichts besseres eingefallen ist.
Ich rufe die interne Funktion __str__ der Klasse Adresse in einer Schleife auf, um die Ausgabe zu realisieren.
Meines erachtens nicht sauber, aber wie geht es sonst ?

Code: Alles auswählen

""" dient zur Strukturierung von optionalen Adressdaten """
class Adresszusatz(object):
    def __init__(self, tür=None, stiege = None, aufgang = None, sonstiges = None):

        if tür:
            self.tür = tür
        else:
            self.tür = ""

        if stiege:
            self.stiege = stiege
        else:
            self.stiege = ""

        if aufgang:
            self.aufgang = aufgang
        else:
            self.aufgang = ""

        if sonstiges:
            self.sonstiges = sonstiges
        else:
            self.sonstiges = ""

    def __str__(self):
        s = ""
         
        if self.tür:
            s += "".join(["Tür: ", self.tür, '\n'])
        if self.stiege:
            s += "Stiege: " + self.stiege + '\n'
        if self.aufgang:
            s += "Aufgang: " + self.aufgang + '\n'
        if self.sonstiges:
            s += "Zusatzinfos: " + self.sonstiges + '\n'
           
        return s

""" da die meisten Adressen auch Festnetz und ein Fax haben, aber eben nicht alle,
    wird eine eigene Klasse definiert """
class Standortdaten(object):
    def __init__(self, tel = None, fax = None):

        if tel:
            self.tel = tel
        else:
            self.tel = ""

        if fax:
            self.fax = fax
        else:
            self.fax = ""
            
    def __str__(self):
        s = ""
         
        if self.tel:
            s += "".join(["Tel: ", self.tel, '\n'])
        if self.fax:
            s += "".join(["Fax: ", self.fax, '\n'])
           
        return s


""" Basisdaten einer Adresse + Adresszusatz + Standortdaten """       
class Adresse(Adresszusatz, Standortdaten):
    
    def __init__(self ,bezeichnung, strasse = None, plz = None, ort = None,
                 land = None,
                 tür = None, stiege = None, aufgang = None, sonstiges = None,
                 tel = None, fax = None):
        
        # Initialisierung der geerbten Objektattribute
        Adresszusatz.__init__(self, tür, stiege, aufgang, sonstiges)
        Standortdaten.__init__(self, tel, fax)

        # Initialisierung der Objektattribute           
        self.bezeichnung=bezeichnung

        if strasse:
            self.strasse = strasse
        else:
            self.strasse = ""

        if land:
            self.land = land
        else:
            self.land = ""
            
        if plz:
            self.plz = plz
        else:
            self.plz = ""

        if ort:
            self.ort = ort
        else:
            self.ort = ""

            
    def __str__(self):
        
        s = ""
         
        if self.strasse:
            s += self.strasse + '\n'
            
        if self.land:
            s += self.land + "-"
            
        if self.plz:
            s += self.plz + " "
            
        if self.ort:
            s += self.ort + '\n'
        # __str__ von den Oberklassen ausführen
        zusatz = Adresszusatz.__str__(self)
        standort = Standortdaten.__str__(self)

        if zusatz != "":
            s += '\n' + zusatz

        if standort != "":
            s += '\n' + standort

        s += "-----------------------------" + '\n'
 
        return s



""" 1 zu * - Beziehung zu Adresse """
class Adressen():
    def __init__(self, daten=None):
        self.adressen = []
        if daten:
            # falls jemand nicht eine [ Adresse ] sondern gleich ein
            # Objekt des Typs Adressen übergibt, soll der Code auch funktionieren
            if isinstance(daten, list) or isinstance(daten, Adressen):
                for eintrag in daten:
                    if isinstance(eintrag, Adresse):
                        self.adressen.append(eintrag)
            elif isinstance(daten, Adresse):
                self.adressen.append(daten)


    # obrige Schleife klappt nur, wenn Adressen auch iterierbar sind !
    def __iter__(self):
        for eintrag in self.adressen:
            yield eintrag
                

    def __str__(self):
        s = ""
        if self.adressen:
            for d in self.adressen:
                s += '\n'.join(['\n', "*************", d.bezeichnung, "*************", '\n'])
                # Hier der kritische Aufruf !!!!!!!!
                s += Adresse.__str__(d)
        return s
                          

class Kontakt(Adressen):
    def __init__(self,vorname, nachname=None,anrede=None, mobil=None, email=None, titel=None, adressen=None):
        
        # Initialisierung der geerbten Objektattribute von Adressen
        Adressen.__init__(self, adressen)

        
        self.vorname=vorname

        if nachname:
            self.nachname = nachname
        else:
            nachname = ""

        if anrede:
            self.anrede = anrede
        else:
            anrede = ""
        
        if mobil:
            self.mobil = mobil
        else:
            self.mobil = ""

        if email:
            self.email = email
        else:
            self.email = ""

        if titel:
            self.titel = titel
        else:
            self.titel = ""
            

    def __str__(self):
        s= ""
        s += "".join([" ", self.titel, " ", '\n', self.vorname, " ", self.nachname,
                      '\n', "email: ", self.email, '\n', "mobil: ", self.mobil])
        oberklasse = Adressen.__str__(self)
        s += oberklasse
        return s
        
        

        
                
        
        

adresse1_sepp = Adresse("Hauptwohnsitz", strasse = "Schillerstrasse 1", ort = "Objekthausen",
                        land = "CH", tel = "4584534345445", plz="9999",
                        fax="454545454", stiege = "3", aufgang = "links")
# Übergabe einer einzigen Adresse
adresse2_sepp = Adresse("Ferienhaus", strasse = "Strandbad 45", ort = "Objekthausen",
                        land = "IT", tel = "4457495749584")
# Falls gleich eine Liste von Adresse übergeben wird
mehrere_adressen_liste = [adresse1_sepp, adresse2_sepp]
# falls die Adressen vom Typ Adressen sind
mehrere_adressen = Adressen([adresse1_sepp, adresse2_sepp])

variante1 = Kontakt("Sepp", "Huber", anrede = "Herr", adressen = adresse1_sepp, mobil="0564545454", email="muster@abc.com")
variante2 = Kontakt("seppo", "Huber", anrede = "Herr", email="nono@nosay.com", adressen = mehrere_adressen_liste)
variante3 = Kontakt("seppo", "Huber", anrede = "Herr", email="nono@nosay.com", adressen = mehrere_adressen)

print ("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++")
print (variante1)
print ("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++")
print (variante2)
print ("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++")
print (variante3)
print ("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++")
Entschuldigung, der Code ist schon etwas lang, dafür aber komplett und ausführbar
Antworten