Class variables

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
Krischu
User
Beiträge: 87
Registriert: Dienstag 14. Januar 2014, 09:07

Ich habe eine Designfrage:

eine Klasse

Code: Alles auswählen

class myProject()

     def __init__ (self,filename,mode) :
          __filename__ = filename
          __mode__ = mode

     def read() :
         f = open(filename,mode)
         return result
  
     def read_header() :
         lies header...


     usw.

Aufrufen will ich die class dann so:

p = myProject('datafile.txt','r')
p.read_header()
a = p.read()
 
Was ich haben möchte sind sowas wie private class variablen (die es aber so in python per Design nicht gibt).

Aber wie kann ich die Variablen __filename__ und __mode__ global machen, so, daß jede member function darauf zugreifen kann?

Ich hatte gedacht, ich krieg's mit den __ hin.
--
Grüße
Christoph
BlackJack

@Krischu: Das da ist keine Klasse. Schau doch mal in einem Tutorial, zum Beispiel in der Python-Dokumentation, wie Klassen und Methoden in Python funktionieren. Und vergiss ”private”. Wenn eine Methode auf etwas zugreifen soll was an das Objekt gebunden ist auf dem die Methode aufgerufen wird dann muss man das ganz normal über einen Attributzugriff auf diesem Objekt machen. Das wird beim Aufruf von Methoden als erstes Argument übergeben und heisst per Konvention `self`.
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

Krischu hat geschrieben:Ich hatte gedacht, ich krieg's mit den __ hin.
Verwende doppelte führende Unterstriche erst dann wenn du erklären kannst, wozu sie gut sind. Und wenn du es erklären kannst, dann wirst du sie normalerweise gar nicht verwenden.

Verwende allerdings niemals(!!!) doppelte führende Unterstriche am Beginn und Ende eines Bezeichnernamens. So aufgebaute Namen sind für den Python-Kern reserviert. Um den Style Guide for Python Code im Kapitel über Naming Styles zu zitieren: "__double_leading_and_trailing_underscore__: 'magic' objects or attributes that live in user-controlled namespaces. E.g. __init__, __import__ or __file__. Never invent such names; only use them as documented."

Grundsätzlich schmeißt du noch einiges durcheinander. Du sagst, dass du Klassenvariablen möchtest, deine Beschreibung klingt aber eher danach, dass es Attribute eines Exemplars einer Klasse sein sollen. Dann hättest du die Attribute gerne global, aber trotzdem privat. :shock: Was dir fehlt sind Grundlagen, aber da gibt es natürlich Abhilfe. Schau dir mal im Tutorial den Abschnitt "Classes" an. Wenn dann Fragen bleiben sollten oder neu aufkommen, dann frag noch mal.
Krischu
User
Beiträge: 87
Registriert: Dienstag 14. Januar 2014, 09:07

So, habe mir mal das Tutorial angesehen und in der Tat hatte ich natürlich einiges falsch gemacht. Sicher vielleicht auch jetzt noch. Habe einige Funktionalität noch mit hineingeschrieben, die ich mal auf konzeptionelle Richtigkeit zur Diskussion stellen wollte. Die runden Klammern in der Klassendefinitionszeile waren falsch, Doppelpunkt hatte ich auch vergessen. Im Original allerdings richtig. Angemeckert wurden die runden Klammern übrigens nicht.

Classvariable war natürlich auch falsch, ich meinte eine Membervariable, die zur Instanz gehört. Insofern ist der Titel jetzt nicht mehr so passend. :)

Code: Alles auswählen

class myProject :
 
     def __init__ (self,filename,mode) :
          self.fn = filename
          self.md = mode
          self.data = []
 
     def read(self) :
         f = open(self.nm,self.md)
          # read from file into data
        for line in f:
             for k in range(0,columns):
                        self.data[i,k] = line[k]
             i = i + 1
         f.close()
         return self.data
 
     def read_header(self) :
         # open file
         # lies header...
         # ermittle rows,columns
         # close file
         return <weiß noch nicht>

 
# Aufrufen will ich die Klasse dann so:
 
p = myProject('datafile.txt','r')
p.read_header()
a = p.read()
print a.shape
Ich brauche in der Instanz ein 2-dimensionales Array, dessen shape allerdings erst nach dem Einlesen der Daten bekannt wird.
Irgendwo im Tutorial sah ich self.data = [], was mir so erschien wie ein leeres Array unbekannter Dimension. Genau das, was ich brauche.
--
Grüße
Christoph
BlackJack

@Krischu: In Python 2 sollte man von `object` erben wenn es sonst keine Basisklasse gibt. Sonst funktionieren einige Sachen wie zum Beispiel `property()` nicht richtig.

Bei der Namensgebung der Klasse möchte ich auf Style Guide for Python Code hinweisen. Was soll der `my`-Präfix beim Klassennamen? Auch für Attributnamen gilt: Nicht Abkürzen und den Leser damit zum Raten zwingen.

Bei einer leeren Liste kann man nicht an irgendwelche Indexe etwas zuweisen und Tupel als Index geht auch nicht. Listen haben auch kein `shape`-Attribut. Und die Klasse ist weder vollständig, noch erscheint sie mir sinnvoll. Vielleicht solltest Du Dich mal auf die Funktionalität von `read()` und `read_header()` konzentrieren statt irgendeine fragwürdige Klasse zu entwerfen. Bis jetzt kann man das jedenfalls ganz einfach mit zwei Funktionen lösen, da braucht man keine Klasse.

`i` wird nicht initialisiert, das würde in Zeile 14 zu einer Ausnahme führen. Wenn man zusätzlich zu den Elementen eines iterierbaren Objekts einen Laufindex haben möchte, kann man den mit der `enumerate()`-Funktion generieren und sich damit das Initialisieren und das manuelle hochzählen ersparen.

Wenn das `shape` auf `numpy`-Arrays hindeuten sollte: `numpy` hat Funktionen zum Einlesen von Datendateien.
Sirius3
User
Beiträge: 17747
Registriert: Sonntag 21. Oktober 2012, 17:20

@Krischu: ich rate Dir das Kapitel über Listen in dem Tutorial Deiner Wahl nochmals genau durchzulesen. Die Einrückungen passen noch nicht. Namen sind unterschiedlich geschrieben, und zu kurz um aussagekräftig zu sein. Für meinen Geschmack ist noch zu viel Freiraum vor ":" und "(" und zu wenig hinter ",". "columns" erscheint aus dem Nichts. Wenn Du eine Laufvariable brauchst, nimm "enumerate" und Dateien sollten mit "with" geöffnet werden. Dass sowohl "self.data" gefüllt, als auch zurückgegeben wird, ist ein unschönes Design. Entweder füllt eine Methode Instanzattribute, oder sie gibt etwas zurück, aber nicht beides.
Krischu
User
Beiträge: 87
Registriert: Dienstag 14. Januar 2014, 09:07

Sirius3 hat geschrieben:@Krischu: ich rate Dir das Kapitel über Listen in dem Tutorial Deiner Wahl nochmals genau durchzulesen. Die Einrückungen passen noch nicht. Namen sind unterschiedlich geschrieben, und zu kurz um aussagekräftig zu sein. Für meinen Geschmack ist noch zu viel Freiraum vor ":" und "(" und zu wenig hinter ",". "columns" erscheint aus dem Nichts. Wenn Du eine Laufvariable brauchst, nimm "enumerate" und Dateien sollten mit "with" geöffnet werden. Dass sowohl "self.data" gefüllt, als auch zurückgegeben wird, ist ein unschönes Design. Entweder füllt eine Methode Instanzattribute, oder sie gibt etwas zurück, aber nicht beides.
@sirius:
Ich bitte Dich, das soll jetzt kein fertiges Programm sein. Auch was die Einrückung betrifft, da könnten die Forumsbetreiber mal was am Editor tun, denn die bei Python so mandatorischen Tabs erlaubt der Editor überhaupt nicht als Eingabe.

Die Designbemerkung hinsichtlich der Rückgabe von Data erkenne ich an. Auch enumerate und with werde ich aufgreifen.

@blackjack:

Namensgebung hier nur völlig willkürlich und zum Zwecke der Anonymisierung. Hinsichtlich der leeren Liste: Ich mache noch ein

self.data = np.zeros((rows,columns))

im Zuge des Einlesens, nachdem ich die Dimension bestimmt habe. Damit dürfte doch das Array (data) richtig dimensioniert sein?

In der Tat arbeite ich mit numpy arrays.

Was Deine Bemerkung hinsichtlich Notwendigkeit einer Klasse betrifft, so gebe ich Dir recht, daß man natürlich auch ein paar Funktionen hätte schreiben können. Damit hatte ich auch angefangen, wollte es aber jetzt doch in eine Klasse kapseln.

Ich weiß, daß man z.B. CSV-Dateien auch mit numpy einlesen kann, nur ist das Format der Datei eben etwas speziell und dafür gibt's eben nichts in numpy.
--
Grüße
Christoph
BlackJack

@Krischu: Es macht aber überhaupt gar keinen *Sinn* das Gezeigte in einer Klasse zu kapseln. Es macht das Programm konzeptionell komplexer ohne das man einen Vorteil davon hätte, und die Daten die dort zusammengefasst werden machen so wie es dort bis jetzt aussieht auch gar keinen Sinn zusammengefasst zu werden.

Eigentlich sollte man ja gerade keine Tabs sondern Leerzeichen verwenden und der Beitragseditor ist sicher nicht geeignet um Quelltext zu schreiben. Auch wenn man JavaScript einbinden würde um das voreingestellte Verhalten des Browsers bezüglich der Tabulator-Taste zu ändern, damit man die Taste zum Einrücken verwenden kann, statt zum Sprung zum nächsten Eingabeelement des HTML-Formulars, ist der Editor denkbar schlecht zum Bearbeiten von Quelltext geeignet. Benutze einen echten Texteditor und kopiere den Text in den Browser oder schau mal ob es für Deinen Browser ein Plugin gibt, welches es erlaubt Texteingabefelder in einem externen Editor Deiner Wahl zu bearbeiten. Das mache ich zum Beispiel mit Firefox und dem „It's All Text”-Plugin so.

Die Zeile in der das Array erstellt wird kann man ja nicht ahnen wenn Du sie nicht zeigst. Allerdings stellt sich dann die Frage warum das Attribut in der `__init__()` mit einer Liste initialisiert wird, die ja gar kein `shape`-Attribut hat. `data` hätte damit im Laufe des Lebens dieses Objekts Werte von verschiedenen Typen mit verschiedenen Eigenschaften. Das sollte man eigentlich vermeiden. Wenn man zu einem bestimmten Zeitpunkt tatsächlich einen Namen oder ein Attribut binden muss ohne dass man den Wert schon hat, nimmt man üblicherseise `None` oder ein „Nullobjekt”, also eines das die Eigenschaften des späteren Werttyps aber keine Funktionalität hat. In diesem Fall beispielsweise ein zweidimensionales Array mit der Grösse 0×0. Allerdings sollte ein Objekt nach Ablauf der `__init__()` in einem benutzbaren Zustand sein und da macht hier weder die Zusammenfassung der Werte noch die Aufteilung der Funktionalität auf Methoden wirklich Sinn.
Krischu
User
Beiträge: 87
Registriert: Dienstag 14. Januar 2014, 09:07

BlackJack hat geschrieben:@Krischu: Es macht aber überhaupt gar keinen *Sinn* das Gezeigte in einer Klasse zu kapseln. Es macht das Programm konzeptionell komplexer ohne das man einen Vorteil davon hätte, und die Daten die dort zusammengefasst werden machen so wie es dort bis jetzt aussieht auch gar keinen Sinn zusammengefasst zu werden.
Über Sinn und Unsinn möchte ich im Moment nicht weiter diskutieren. Ich will nunmal später ein Objekt haben, das ich mit Methoden befragen kann. Im übrigen benutze ich es als Übung. Das Projekt ist außerdem eine Parallelentwicklung zu einer bestehenden gleichen Klasse in C++, bei der ich die gleiche Kritik hätte anbringen könnte, weil man es auch ohne OO in C Functions hätte machen können.
Eigentlich sollte man ja gerade keine Tabs sondern Leerzeichen verwenden und der Beitragseditor ist sicher nicht geeignet um Quelltext zu schreiben. Auch wenn man JavaScript einbinden würde um das voreingestellte Verhalten des Browsers bezüglich der Tabulator-Taste zu ändern, damit man die Taste zum Einrücken verwenden kann, statt zum Sprung zum nächsten Eingabeelement des HTML-Formulars, ist der Editor denkbar schlecht zum Bearbeiten von Quelltext geeignet. Benutze einen echten Texteditor und kopiere den Text in den Browser oder schau mal ob es für Deinen Browser ein Plugin gibt, welches es erlaubt Texteingabefelder in einem externen Editor Deiner Wahl zu bearbeiten. Das mache ich zum Beispiel mit Firefox und dem „It's All Text”-Plugin so.
Danke für den Hinweis zum „It's All Text”-Plugin. Werde ich mir mal ansehen, auch zu anderen Zwecken vielleicht nützlich.

Ich benutze iPython Notebooks und wenn ich mit Cut/Paste hier in den Forumseditor hineinpaste, dann werden die Tabs nicht dargestellt und mühsame Editierarbeit ist die Folge. Ich bleibe bei meiner Kritik am Forumseditor, der nicht Pythongerecht ist.
Mal abgesehen von der Ästhetik (gestrichelte Linien zw. den Zeilen und ziemlich raumgreifend). Da habe ich schon schönere Python-Listings gesehen :). Aber der wird wahrscheinlich vom PhpBB mitgeliefert, weiß nicht.


Die Zeile in der das Array erstellt wird kann man ja nicht ahnen wenn Du sie nicht zeigst. Allerdings stellt sich dann die Frage warum das Attribut in der `__init__()` mit einer Liste initialisiert wird, die ja gar kein `shape`-Attribut hat. `data` hätte damit im Laufe des Lebens dieses Objekts Werte von verschiedenen Typen mit verschiedenen Eigenschaften. Das sollte man eigentlich vermeiden. Wenn man zu einem bestimmten Zeitpunkt tatsächlich einen Namen oder ein Attribut binden muss ohne dass man den Wert schon hat, nimmt man üblicherseise `None` oder ein „Nullobjekt”, also eines das die Eigenschaften des späteren Werttyps aber keine Funktionalität hat. In diesem Fall beispielsweise ein zweidimensionales Array mit der Grösse 0×0. Allerdings sollte ein Objekt nach Ablauf der `__init__()` in einem benutzbaren Zustand sein und da macht hier weder die Zusammenfassung der Werte noch die Aufteilung der Funktionalität auf Methoden wirklich Sinn.
Letzteres (data) habe ich korrigiert und mit self.data = None initialisiert.
Danke für den Hinweis.
--
Grüße
Christoph
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

Krischu hat geschrieben:Ich benutze iPython Notebooks und wenn ich mit Cut/Paste hier in den Forumseditor hineinpaste, dann werden die Tabs nicht dargestellt und mühsame Editierarbeit ist die Folge. Ich bleibe bei meiner Kritik am Forumseditor, der nicht Pythongerecht ist.
Der Style Guide for Python Code ist im Kapitel "Tabs or Spaces?"ziemlich deutlich was die Verwendung von Tabulatoren angeht: "Spaces are the preferred indentation method. Tabs should be used solely to remain consistent with code that is already indented with tabs." Wenn du also bewusst gegen den Style Guide verstößt, dann wundere dich nicht, wenn sowohl andere Entwickler wie auch andere Software damit unglücklich sind.
BlackJack

@Krischu: Wenn man OOP übt gehört auch dazu es sinnvoll einzusetzen, also zu lernen wann man es einsetzt, und wann eben nicht. Denn sonst ist es kein OOP. Einfach nur irgendwelchen Code in Klassen zu stecken macht ein Programm nicht automatisch objektorientiert. Das in einer anderen Sprache eine ähnliche Klasse programmiert wurde ist kein Grund den Entwurf einfach so zu übernehmen. Die eine oder andere Gepflogenheit in einer Sprache macht in einer anderen nicht zwingend Sinn oder kann dort zumindest so unüblich sein, dass es Programmierer die in der Sprache programmieren, verwirrt. Diese komische Zweiteilung in der `__init__()` das Objekt gar nicht *richtig* zu initialisieren sondern dafür eine zusätzliche Methode bereit zu stellen sieht man in C++ öfter weil man dort vom Konstruktor schlecht einen eventuell auftretenden Fehler an den Aufrufer übermittelt bekommt. Das ist bei Python aber kein Problem, denn da benutzt man einfach Ausnahmen für.
Krischu
User
Beiträge: 87
Registriert: Dienstag 14. Januar 2014, 09:07

BlackJack hat geschrieben:@Krischu: Wenn man OOP übt gehört auch dazu es sinnvoll einzusetzen, also zu lernen wann man es einsetzt, und wann eben nicht. Denn sonst ist es kein OOP. Einfach nur irgendwelchen Code in Klassen zu stecken macht ein Programm nicht automatisch objektorientiert. Das in einer anderen Sprache eine ähnliche Klasse programmiert wurde ist kein Grund den Entwurf einfach so zu übernehmen. Die eine oder andere Gepflogenheit in einer Sprache macht in einer anderen nicht zwingend Sinn oder kann dort zumindest so unüblich sein, dass es Programmierer die in der Sprache programmieren, verwirrt. Diese komische Zweiteilung in der `__init__()` das Objekt gar nicht *richtig* zu initialisieren sondern dafür eine zusätzliche Methode bereit zu stellen sieht man in C++ öfter weil man dort vom Konstruktor schlecht einen eventuell auftretenden Fehler an den Aufrufer übermittelt bekommt. Das ist bei Python aber kein Problem, denn da benutzt man einfach Ausnahmen für.

24 Jahre ist es her, daß ich erstmalig Objective-C einsetzte und ein größeres Projekt darin schrieb. Und ich habe OO verinnerlicht damals. Ich kenne OO-Design. Ich meinte mit Üben, es in Python üben.
--
Grüße
Christoph
BlackJack

@Krischu: Das ändert jetzt eigentlich nichts an dem gesagten, denn es in Python zu üben heisst auch nicht sinnfrei irgendwelche Klassen zu definieren die den Code unnötig komplexer zu machen. Und wenn ich mir anschaue *was* Du da in der Klasse zusammengefasst und wie die Aufteilung der Methoden ist: Das wäre in anderen Sprachen IMHO auch kein guter OOP-Entwurf, also selbst in Java nicht, wo man ja alles in Klassen stecken *muss*.
Krischu
User
Beiträge: 87
Registriert: Dienstag 14. Januar 2014, 09:07

BlackJack hat geschrieben:@Krischu: Das ändert jetzt eigentlich nichts an dem gesagten, denn es in Python zu üben heisst auch nicht sinnfrei irgendwelche Klassen zu definieren die den Code unnötig komplexer zu machen. Und wenn ich mir anschaue *was* Du da in der Klasse zusammengefasst und wie die Aufteilung der Methoden ist: Das wäre in anderen Sprachen IMHO auch kein guter OOP-Entwurf, also selbst in Java nicht, wo man ja alles in Klassen stecken *muss*.
Ich wollte nichts großes entwerfen. Diese Klasse hätte, wie hier schon festgestellt wurde, auch als module (Sammlung von functions) geschrieben qwerden können, aber ich wollte einfach mal eine Klasse machen und dabei die Syntax lernen. Das Projekt ist beendet. Die __init__ Methode initialisert jetzt auch richtig. Ich habe ein paar Member (data, columns, rows, header)
und methods getdata(), getheader().

Das einzige, was mich stört, ist, daß __init__ offenbar None zurückgeben muß. Da kann man jetzt auch wieder lange drüber diskutieren, was es denn anderes zurückgeben sollte/dürfte.
--
Grüße
Christoph
BlackJack

@Krischu: In der Tat, denn was sollte eine Methode die explizit zum Initialisieren des übergebenen Objekts vorgesehen ist schon zurückgeben, und an wen?

*Warum* würdest Du denn dort etwas anderes zurück geben wollen und was? Wenn ich raten müsste ist das etwas was man mit Klassenmethoden löst (`classmethod()`).
Krischu
User
Beiträge: 87
Registriert: Dienstag 14. Januar 2014, 09:07

BlackJack hat geschrieben:@Krischu: In der Tat, denn was sollte eine Methode die explizit zum Initialisieren des übergebenen Objekts vorgesehen ist schon zurückgeben, und an wen?

*Warum* würdest Du denn dort etwas anderes zurück geben wollen und was? Wenn ich raten müsste ist das etwas was man mit Klassenmethoden löst (`classmethod()`).
Das wichtigste an der Klasse ist eigentlich die Tatsache, daß sie ein Array erzeugt,in dem sich Daten befinden, die aus einem File eingelesen wurden. Eigentlich könnte die Klasse von Array abgeleitet sein. Dann sollte das Objekt das Array zurückgeben, nachdem es initialisiert wurde.
--
Grüße
Christoph
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Krischu hat geschrieben:Dann sollte das Objekt das Array zurückgeben, nachdem es initialisiert wurde.
Und wohin sollte es zurückgegeben werden? Wenn __init__ aufgerufen wird, dann existiert das Objekt schon.
Das Leben ist wie ein Tennisball.
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

Krischu hat geschrieben:Eigentlich könnte die Klasse von Array abgeleitet sein. Dann sollte das Objekt das Array zurückgeben, nachdem es initialisiert wurde.
Sei Array eine Liste, dann kannst Du dies z.B. so erreichen:

Code: Alles auswählen

class Array(list):
    def __init__(self, filename):
        super(Array, self).__init__()
        # read from file ...
        
a = Array(filename)
Es spricht auch nichts dagegen die Liste als Attribut von Array (has a) zu führen. Das würde ich zumeist auch empfehlen, denn das direkte Ableiten von Python-Datenstrukturen kann tückisch sein, da Du dann ggf. auch auf das korrekte Überladen der 'special methods' zu achten hast.
Sirius3
User
Beiträge: 17747
Registriert: Sonntag 21. Oktober 2012, 17:20

@Krischu: da »__init__« eindeutig die innere Struktur des Objekts verändert, widerspricht es auch dem OOP-Prinzip, Zuständigkeiten zu trennen, wenn »__init__« auch noch etwas zurückgeben würde.
Antworten