Frage zu Pythons OOP :)

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.
EnTeQuAk
User
Beiträge: 986
Registriert: Freitag 21. Juli 2006, 15:03
Wohnort: Berlin
Kontaktdaten:

So Jungz und Mädels :)

Erstmal: Marry Cristmas to all of you!!! und ne schöne Beschehrung! :)

Und nu zu meinem Problem:

Ich bin gerade dabei, ein paar Module umzuschreiben und nun mich entlich mal in die Welt des OOP begeben :)

Doch nun habe ich mal folgenden Testcode:

Code: Alles auswählen

class MainParser(LogParser):
    def __init__(self):
        self.projectName = self.get_project_name()
    
    
    def get_project_name(self):
        print 'Bitte gib den Projektnamen ein'
        projectName = raw_input('Eingabe: ')
        print '-----------------------------\n'
        return projectName
    
    def get_project_files(self):
        print self.projectName
        
    
    
    

dauCMSApp = MainParser()

if __name__ == '__main__':
    print dauCMSApp.get_project_files()
Nun bekomme ich beim Ausfüren folgende Ausgabe:

Code: Alles auswählen

Bitte gib den Projektnamen ein
Eingabe: test
-----------------------------

test
None
Und nun wundere ich mich, woher das '' None '' kommt :)

Könnt ihr mich da aufklären?

MfG EnTeQuAk
Benutzeravatar
C4S3
User
Beiträge: 292
Registriert: Donnerstag 21. September 2006, 10:07
Wohnort: Oberösterreich

Also ich bin ja ein absoluter noob, aber:

sollte bei

Code: Alles auswählen

return projectName
nicht eigentlich

Code: Alles auswählen

return self.projectName
stehen?
Mich wundert es, dass da kein Fehler vom Interpreter kommt. Ich habe jetzt ein paar Tage mit Tkinter rumgespielt und immer, wenn ich in einer Klasse das self. vergessen habe, hat Python gemeckert.
Gruß!
Marten2k
User
Beiträge: 4
Registriert: Mittwoch 25. Januar 2006, 18:48

Hallo,

hier mal der korrigierte Code:

Code: Alles auswählen

class MainParser(LogParser):
    def __init__(self):
        self.projectName = self.get_project_name()
    
    
    def get_project_name(self):
        print 'Bitte gib den Projektnamen ein'
        projectName = raw_input('Eingabe: ')
        print '-----------------------------\n'
        return projectName
    
    def get_project_files(self):
        # HIER SOLLTE EIN return self.projectName stehen
         print self.projectName
        
    
    
    

dauCMSApp = MainParser()

if __name__ == '__main__':
    # DIESESR print ergibt None, weil oben das return fehlt und
    # somit die Funktion nichts zurueckgibt !
    print dauCMSApp.get_project_files()
Gruss
Marten
BlackJack

EnTeQuAk hat geschrieben:Ich bin gerade dabei, ein paar Module umzuschreiben und nun mich entlich mal in die Welt des OOP begeben :)
Bitte aufpassen, dass es wirklich Sinn macht und nicht einfach alles in Klassen stecken.
Nun bekomme ich beim Ausfüren folgende Ausgabe:

Code: Alles auswählen

Bitte gib den Projektnamen ein
Eingabe: test
-----------------------------

test
None
Und nun wundere ich mich, woher das '' None '' kommt :)
Das hat nichts mit OOP zu tun. Jede Methode gibt genau wie jede Funktion *immer* etwas zurück. Wenn man das nicht explizit mit ``return`` macht, dann wird `None` zurückgegeben wenn der Programmablauf am Ende einer Funktion ankommt. Und genau dieses `None` gibst Du aus.
EnTeQuAk
User
Beiträge: 986
Registriert: Freitag 21. Juli 2006, 15:03
Wohnort: Berlin
Kontaktdaten:

Ohh :) Herzlichen Dank! :)

Gut dann habe ich das nun auch verstanden :)


Bitte aufpassen, dass es wirklich Sinn macht und nicht einfach alles in Klassen stecken.
Jupp Mach ich! :D Das ist ja das "schwere", an OOP -- aber wenn es läuft is es ne feine Sache ;)

MfG EnTeQuAk
Y0Gi
User
Beiträge: 1454
Registriert: Freitag 22. September 2006, 23:05
Wohnort: ja

Du musst in einer Methode (quasi eine Funktion eines Objektes) nicht lokal Werte annehmen und zurückgeben, wie du es in get_project_name() getan hast, sondern kannst und solltest die Möglichkeit nutzen, direkt auf Objektvariablen zugreifen zu können.

Aus:

Code: Alles auswählen

class MainParser(LogParser):

    def __init__(self):
        self.projectName = self.get_project_name()
    
    def get_project_name(self):
        print 'Bitte gib den Projektnamen ein'
        projectName = raw_input('Eingabe: ')
        print '-----------------------------\n'
        return projectName
wird dann (sinnvoll umbenannt, da get_* und set_* traditionell etwas anderes tun):

Code: Alles auswählen

class MainParser(LogParser):

    def __init__(self):
        self.projectName = ''

        # direkt bei der Instanziierung Projektnamen abfragen
        self.ask_for_new_project_name()
    
    def ask_for_new_project_name(self):
        print 'Bitte gib den Projektnamen ein'
        self.project_name = raw_input('Eingabe: ')

    def display_project_name(self):
        print '-----------------------------\n'
        print self.project_name

Der Code ist übrigens unvollständig, wo kommt LogParser her?
EnTeQuAk
User
Beiträge: 986
Registriert: Freitag 21. Juli 2006, 15:03
Wohnort: Berlin
Kontaktdaten:

LogParser war eine Klasse, die mir einige Hilfsmethoden für die Erzeugung einer *.log Datei gegeben hat.

Allerdings habe ich das ganze nun doch anders gemacht. Aber deinen Tipp habe ich mit eingebaut ;)

Allerdings, überlege ich, ob die vorherige Zuweisung:

Code: Alles auswählen


    def __init__(self):
        self.projectName = ''
Wirklich nötig ist.

Denn erstelle ich nicht mit

Code: Alles auswählen


    def ask_for_new_project_name(self):
        print 'Bitte gib den Projektnamen ein'
        self.project_name = raw_input('Eingabe: ')
Das Attribut, sodass ich '' self.projectName '' (wo du übrigens einen Schreibfehler drinne hattest ;) ) gar nicht in die '' __init__ '' Methode direkt schreiben muss oder?

MfG EnTeQuAk

edit: sorry in Klassen heißen die Dinger nicht Variablen sonder Attribute ;)
Y0Gi
User
Beiträge: 1454
Registriert: Freitag 22. September 2006, 23:05
Wohnort: ja

Der "Schreibfehler" rührt daher, dass ich `projectName` PEP8-konform in `project_name` umbenennen wollte, aber den Namen im Konstruktor übersehen hatte.

> "sorry in Klassen heißen die Dinger nicht Variablen sonder Attribute"
Das sind nachwievor Variablen, nur stellen die hier eben die Klassen- oder Objektattribute dar; manchmal auch Membervariablen genannt.

> "[...] gar nicht in die '' __init__ '' Methode direkt schreiben muss oder?"
Es ist immer anzuraten, alle Instanzvariablen (und wieder eine andere Bezeichnung) im Konstruktor zu definieren, egal ob sie schon dort oder erst später einen sinnvollen Wert zugewiesen bekommen. In diesem konkreten Fall wäre diese Definition aber in der Tat unnötig, weil ja im Konstruktor zwangsweise eine Methode zum Setzen eines Namens aufgerufen wird.
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

Y0Gi hat geschrieben: Es ist immer anzuraten, alle Instanzvariablen (und wieder eine andere Bezeichnung) im Konstruktor zu definieren, egal ob sie schon dort oder erst später einen sinnvollen Wert zugewiesen bekommen.
Korrekt! *unterschreib*

Es gibts fast nichts unübersichtlicheres (abgesehen von globals) wenn Atribute kreuz und quer erst eingeführt werden. Ich habe es gerne wenn alle Attribute in vornherein im Konstruktor definiert werden. Unabhängig davon ob sie da schon einen richtigen Wert bekommen ;) Dann eine gute Beschreibung zu den Attributen (in http://docutils.sourceforge.net/docs/us ... paragraphs -> #: Meine Beschreibung) und schon weiß man alles, was die Klasse verwendet! Private Attribute kann man mit einem unterstrich makieren damit der Anwender der Klasse weiß, das davon die finger zu lassen sind ;) Ja ich weiß, ich und meine Bösen Unterstriche (was übrigens 80% der Python programmiere nutzen um Privates (_) oder Protected (__)) zu signalisieren :P

lg
EnTeQuAk
User
Beiträge: 986
Registriert: Freitag 21. Juli 2006, 15:03
Wohnort: Berlin
Kontaktdaten:

Danke für die schönen Kommentare.

Aber eine Frage: Der link, sape, ist der jetzt ernst gemeint, das der auf die Quickreferenz von docutils zeigt? :)


Ansonsten komme ich so langsam aber sicher hinter das ganze OOP-Zeugs ;)

Und das mit dem Defninieren der Atribute habe ich wiefolgt umgesetzt:

Code: Alles auswählen

class MainParser:
    def __init__(self):
        # define attributes
        self.project_name = ''
        self.project_path = ''
        self.project_files = []
        self.rendered_project_path = ''

        # fill atributes with content ;)
        self.set_project_name() # set self.project_name
        self.set_project_path()  # set self.project_path
        self.set_project_files()   # set self.project_files
        self.set_rendered_project_path() # set self.rendered_project_path
Ich denke, das ist das, was ihr meintet oder? :)

Und ich muss sagen - so kann man sich eine sehr schöne Struktur aufbauen und sieht vorallem, was welches Atrubut für einen Typ hat.
(siehe self.project_files - da sieht man sofort, das man es mit einer Liste zu tun hat und braucht gar net runterscrollen ;) )

Ach ja :)

Dankeee!

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

sape hat geschrieben:(was übrigens 80% der Python programmiere nutzen um Privates (_) oder Protected (__)) zu signalisieren :P
Die 80% kann ich inbesondere bei __ schwer glauben. Hast du dafür Belege?
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
BlackJack

EnTeQuAk hat geschrieben:Und das mit dem Defninieren der Atribute habe ich wiefolgt umgesetzt:

Code: Alles auswählen

class MainParser:
    def __init__(self):
        # define attributes
        self.project_name = ''
        self.project_path = ''
        self.project_files = []
        self.rendered_project_path = ''

        # fill atributes with content ;)
        self.set_project_name() # set self.project_name
        self.set_project_path()  # set self.project_path
        self.set_project_files()   # set self.project_files
        self.set_rendered_project_path() # set self.rendered_project_path
Ich denke, das ist das, was ihr meintet oder? :)
Sieht ein wenig "komisch" aus. Warum gibt es diese ganzen `set_*`-Methoden? Und kann man das nicht so umschreiben, das folgendes ginge:

Code: Alles auswählen

    def __init__(self):
        self.project_name = self.generate_project_name()
        self.project_path = self.create_project_path()
        self.project_files = self.collect_project_files()
        self.rendered_project_path = self.whatever_rendered_project_path()
Und wenn die Methoden nur bei der Initialisierung benötigt werden, könnte man ihre Namen mit einem Unterstrich beginnen.
Und ich muss sagen - so kann man sich eine sehr schöne Struktur aufbauen und sieht vorallem, was welches Atrubut für einen Typ hat.
(siehe self.project_files - da sieht man sofort, das man es mit einer Liste zu tun hat und braucht gar net runterscrollen ;) )
Du siehst nur den Typ des Objekts das an das Attribut gebunden wurde, kurz bevor es vielleicht bei einem Aufruf einer der `set_*`-Methoden an ein anderes gebunden wird.

Wenn ich Attribute temporär an etwas anderes binde, damit alle Namen schon in der `__init__()` eingeführt werden, auch wenn das Attribut erst später einen sinnvollen Wert bekommt, nehme ich normalerweise `None`. Das fliegt einem dann meistens eher um die Ohren wenn man Fehler macht, als wenn man zum Beispiel eine leere Zeichenkette nimmt mit der dann fälschlicherweise weitergearbeitet wird, obwohl sie eigentlich vorher durch etwas sinnvolleres hätte ersetzt werden müssen.
BlackJack

Leonidas hat geschrieben:
sape hat geschrieben:(was übrigens 80% der Python programmiere nutzen um Privates (_) oder Protected (__)) zu signalisieren :P
Die 80% kann ich inbesondere bei __ schwer glauben. Hast du dafür Belege?
Belege habe ich auch keine aber ich würde sagen es sind mehr als 80%. Ich lese da jedenfalls heraus, dass 80% der Python-Programmierer mit _ und __ "private" und "protected" meinen. Was nicht heisst das 80% dauernd überall Unterstriche verwenden; aber *wenn*, dann ist die Bedeutung recht klar. ;-)
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

EnTeQuAk hat geschrieben: Aber eine Frage: Der link, sape, ist der jetzt ernst gemeint, das der auf die Quickreferenz von docutils zeigt? :)
Ja warum :? ich benutze seit neustem auch reStructuredText für die Dokumentation der Klassen/Funktionen, etc. Aber eigentlich wollte ich auf http://docutils.sourceforge.net/rst.html verweisen.
EnTeQuAk hat geschrieben: Und das mit dem Defninieren der Atribute habe ich wiefolgt umgesetzt:

Code: Alles auswählen

class MainParser:
    def __init__(self):
        # define attributes
        self.project_name = ''
        self.project_path = ''
        self.project_files = []
        self.rendered_project_path = ''

        # fill atributes with content ;)
        self.set_project_name() # set self.project_name
        self.set_project_path()  # set self.project_path
        self.set_project_files()   # set self.project_files
        self.set_rendered_project_path() # set self.rendered_project_path
Ich denke, das ist das, was ihr meintet oder? :)
Jepp, so mag ich das :) Aber auch nur fast ;) Mach aus den ``setern`` gleich richtige ``geter`` so das du z.B. ``self.project_name = self.set_project_name()`` schreiben kannst :)
EnTeQuAk hat geschrieben: Und ich muss sagen - so kann man sich eine sehr schöne Struktur aufbauen und sieht vorallem, was welches Atrubut für einen Typ hat.
Genau :D Alles andere finde ich ein wenig unübersichtlich (EDIT: Das hat für mich aber nichts damit zu tun das ich weiß welcher Typ das Attribut hat. Das wird bei mir ehe oben bei der Beschreibung angegeben.)
Leonidas hat geschrieben:
sape hat geschrieben:(was übrigens 80% der Python programmiere nutzen um Privates (_) oder Protected (__)) zu signalisieren :P
Die 80% kann ich inbesondere bei __ schwer glauben. Hast du dafür Belege?
Da hast du mich falsch verstanden. Ich meinte damit nicht das die Pythonprogrammierer 80% der ganze zeit den Code mit _ und __ vermurksen, sondern das 80% der Python Programmierer mit einem _ am Anfang Signalisieren wollen das es Private ist und mit __ Protected. Ich benenne tatsächlich (großzügig geschätzt) ca. 10-20% vom den ganzen Methoden-/Klassen-/Attribut-/Funktionsdefinitionen. Ca. 80-90% der Definitionen fangen bei mir auch tatsächlich ohne _ und __ an. Und __ habe ich bisher noch gar nicht benutzt, da ich bisher keine Sachen brauchte die Protected sind.

...

Warum nutze ich private? Weil es unter Umständen sein kann, dass eine Methode länger wird (und zuviel auf einmal macht) und ich sie in mehrer in sich abgeschlossene Einheiten schreibe, die aber nur für die Hauptmethode(n), die diese verwenden, einen Sinn ergibt und außerhalb der Klasse nicht zu gebrauchen ist. Wenn dabei allgemein verwendbare Methoden rauskommen, kommt das ehe bei mir aus der Klasse raus und wandert in eine `util.py` Datei.

Ein anderer Grund kann sein, wie BlackJack gerade aufgezeigt hat, das es sich um Initialisierungsmethoden für Attribute einer Klasse handelt, die im Konstruktor verwendet werden. Dort finde ich das auch passend es mit einen _ beginne zu lassen.

Und mal ehrlich, ich finde bei Python fast nichts furchtbarer, als wenn ich Methoden/Attribute sehe, bei denen ich nicht unterscheiden kann, welche von außen zu benutzen sind und welche nur intern für die Klasse gedacht sind. Mir geht das da um die Übersichtlichkeiten und den "Workflow" der sich daraus ergibt.

Code: Alles auswählen

Wenn ich Attribute temporär an etwas anderes binde, damit alle Namen schon in der `__init__()` eingeführt werden, auch wenn das Attribut erst später einen sinnvollen Wert bekommt, nehme ich normalerweise `None`. Das fliegt einem dann meistens eher um die Ohren wenn man Fehler macht, als wenn man zum Beispiel eine leere Zeichenkette nimmt mit der dann fälschlicherweise weitergearbeitet wird, obwohl sie eigentlich vorher durch etwas sinnvolleres hätte ersetzt werden müssen.
Was spricht den dagegen gleich den richtigen Type zu verwenden wenn ehe klar ist wie der Zieltyp auszusehen ist?Wenn ich z.B. ein Attribut habe das sich ``self.html_code`` nennt, kann ich das doch gleich mit ``self.html_code = '' `` initialisieren, anstatt ``None`` zu benutzen. Es steht ja ehe für mich in vornherein fest das es ein ``str`` sein wird.
BlackJack

sape hat geschrieben:

Code: Alles auswählen

Wenn ich Attribute temporär an etwas anderes binde, damit alle Namen schon in der `__init__()` eingeführt werden, auch wenn das Attribut erst später einen sinnvollen Wert bekommt, nehme ich normalerweise `None`. Das fliegt einem dann meistens eher um die Ohren wenn man Fehler macht, als wenn man zum Beispiel eine leere Zeichenkette nimmt mit der dann fälschlicherweise weitergearbeitet wird, obwohl sie eigentlich vorher durch etwas sinnvolleres hätte ersetzt werden müssen.
Was spricht den dagegen gleich den richtigen Type zu verwenden wenn ehe klar ist wie der Zieltyp auszusehen ist?Wenn ich z.B. ein Attribut habe das sich ``self.html_code`` nennt, kann ich das doch gleich mit ``self.html_code = '' `` initialisieren, anstatt ``None`` zu benutzen. Es steht ja ehe für mich in vornherein fest das es ein ``str`` sein wird.
Es ging mir nicht um den Typ. Angefangen hat das hier doch mit der Frage was man mit Attributen machen soll, die eigentlich erst in anderen Methoden als der `__init__()` gesetzt werden. Davon sind solche, die man in der `__init__()` schon sinnvoll vorbelegen kann, ja ausgeschlossen, denn dann macht man ja einfach genau das und hat das Problem gar nicht erst. Wenn es Sinn macht, dass man auch mit einer leeren Zeichenkette als `self.html_code` arbeiten kann, dann ist das kein Problem. Aber wenn man zum Beispiel ein `self.filename` hat, das man nicht in der `__init__()` setzt, dann benutze ich `None` und nicht eine leere Zeichenkette, auch wenn das wohl der richtige Typ wäre. Ich möchte halt das der Code auf die Nase fällt wenn eine Methode darauf zugreift bevor der Name richtig gesetzt ist, anstatt einfach Dateien mit dem Namen '' anzulegen.
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

Huch, stimmt eigentlich. Bei solchen Sachen habe ich dann sowas gemacht in der Methode ``if self.filename != ''``. Aber die Methode mit None scheint mir die bessere Lösung. Naja, dann muss ich mal nun alles was so ist durch ``if self.filename is not None`` ersetzen.

Danke.

Und da es nun aktuell ist: Einen schönen rutsch ins neue Jahr wünsche ich euch allen :)

lg
sape
EnTeQuAk
User
Beiträge: 986
Registriert: Freitag 21. Juli 2006, 15:03
Wohnort: Berlin
Kontaktdaten:

Also ihr macht wohl gerne Nachtschichten ;)

Ansonsten finde ich das, das eine schöne Disskussion war/ist :)

Merke schon, hier gibt es einige verschiedene Ansichtsweisen und verwendungsweisen. --- wäre eigentlich auch schlimm, wenn nicht oder? :)


Also ich habe mir jetzt folgende Sachen gemerkt, was hier einige machen:

Methiode1:

Code: Alles auswählen

class Test:
    def __init__(self):
        self.test = self.get_test()

    def get_test(self):
        self.test = 'DADA'
        return self.test
Methode2:

Code: Alles auswählen

class Test:
    def __init__(self):
        self.test = self.get_test()

    def get_test(self):
        test = 'DADA'  ## zuweisung irgenteiner Art - ansonsten gleich in ''return'' rein
        return test
Methode3

Code: Alles auswählen

class Test:
    def __init__(self):
        self.test = '' # oder None
        self.get_test()

    def get_test(self):
        self.test = 'DADA'
Also ich bekenne mich jetzt einfach mal zur ersten oder zweiten :) kann mich da noch nicht so richtig entscheiden... :) --> zweitere könnte bei sehr großen Konstruktormethoden recht Lang und evtl. etwas unüberlichtlich werden - denke ich.

Eure Meinungen?

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

Die erste Methode ist natürlich quatsch, in der wird in get_test self.test = 'DADA' gesetzt, self.test zurückgegeben um dann in __init__ nochmal gleich 'DADA' gesetzt zu werden. Doppelt gesetzt, ergo: Unnütz.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Y0Gi
User
Beiträge: 1454
Registriert: Freitag 22. September 2006, 23:05
Wohnort: ja

An dieser Stelle sei der Artikel Getters/Setters/Fuxors eingeworfen, ein *absolutes* Must-Read!
EnTeQuAk
User
Beiträge: 986
Registriert: Freitag 21. Juli 2006, 15:03
Wohnort: Berlin
Kontaktdaten:

sehr schöner Artikel.

Aber letzentlich ist das auch wieder ein klein wenig Geschmackssache.

Die einen Definieren sofort und weisen dann zu die andere weisen in der definition zu usw....


Danke! Danke! und einen guten Rutsch ins neue Jahr wünscht...

EnTeQuAk
Antworten