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:

Frage zu Pythons OOP :)

Beitragvon EnTeQuAk » Sonntag 24. Dezember 2006, 21:53

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

Beitragvon C4S3 » Sonntag 24. Dezember 2006, 22:01

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

Re: Frage zu Pythons OOP :)

Beitragvon Marten2k » Sonntag 24. Dezember 2006, 22:08

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

Re: Frage zu Pythons OOP :)

Beitragvon BlackJack » Sonntag 24. Dezember 2006, 22:08

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:

Beitragvon EnTeQuAk » Montag 25. Dezember 2006, 10:19

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

Re: Frage zu Pythons OOP :)

Beitragvon Y0Gi » Samstag 30. Dezember 2006, 07:26

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:

Beitragvon EnTeQuAk » Samstag 30. Dezember 2006, 10:41

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

Beitragvon Y0Gi » Samstag 30. Dezember 2006, 11:29

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

Beitragvon sape » Samstag 30. Dezember 2006, 17:44

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:

Beitragvon EnTeQuAk » Samstag 30. Dezember 2006, 19:07

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
Benutzeravatar
Leonidas
Administrator
Beiträge: 16023
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Beitragvon Leonidas » Samstag 30. Dezember 2006, 19:07

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 Modvoice
BlackJack

Beitragvon BlackJack » Samstag 30. Dezember 2006, 20:05

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

Beitragvon BlackJack » Samstag 30. Dezember 2006, 20:11

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

Beitragvon sape » Samstag 30. Dezember 2006, 23:02

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

Beitragvon BlackJack » Sonntag 31. Dezember 2006, 00:06

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.

Wer ist online?

Mitglieder in diesem Forum: Google [Bot]