OOP: Methoden im Kosntruktor aufrufen

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
Frokuss
User
Beiträge: 16
Registriert: Mittwoch 5. Juni 2019, 20:35

Donnerstag 6. Juni 2019, 00:39

Wunderschönen guten Abend,

ich habe gestern das erste mal in meinem Leben Python gesehen... Leider schaffe ich es seit Stunden nicht eine Methode im Konstruktor aufzurufen. Diese sind, warum auch immer, nicht definiert. Vielleicht könnt ihr mir ja da etwas weiter helfen...

Das ganze ist in der datenEinlesen.py gespeichert:

Code: Alles auswählen

import os								#fuer das importieren
import pandas as tab							#fuer die Tabellen
import numpy as np							#fuer KEINE AHNUNG
import re								#fuer regulaere Ausdruecke (gut dass es das gibt!)
#import csv								#Es gibt sogar eine CSV-Bib... Danke fuer die Info!

class DatenEinlesen(object):

	fileString = None
	_fileName_ = None
	_pathName_ = None
	metas = None
	seperator = None
	merkmale = None
    
##############################################################################	
	def __init__(self, pathName, fileName, sep = None):			#sep = optionales Trennzeichen
		datenEinlesen.SetFile(fileName)
		SetPath(pathName)
		a = 12
	
		#self.metas = GetMetas(self.filePath)
		#self.seperator = self.metas[0]
		#self.merkmale = self.metas[1]
		
		#data_path = os.path.join("dataset", self.filePath, sep=metas[0])

##############################################################################	     
	def SetPath(self, path):
		self.pathName = path
		if self._fileName_ != None:
			self.fileString = __DirWithFile__(self._pathName_, self._fileName_)  
   
	def SetFile(file):
		self._fileName_ = file
		if self._pathName_ != None:
			self.fileString = __DirWithFile__(self._pathName_, self._fileName_)
Jetzt versuche ich dieses Zeug über die Jupyter Oberfläche aufzurufen (ist so eine ipynb - Datei):

Code: Alles auswählen

from datenEinlesen import DatenEinlesen
db = DatenEinlesen("./dataset/", "children.csv")
Dabei bekomme ich aber folgenden Fehler angezeigt:

Code: Alles auswählen

---> 19                 DatenEinlesen.SetFile(fileName)
     20                 SetPath(pathName)
     21                 a = 12

NameError: name 'SetFile' is not defined
Leider hat mir Dr. Google nichts brauchbares geliefert. Alles Konstruktoren-Beispiele waren nur mit zuweisen von Attributen deklariert... Versucht habe ich sowas wie:
self.SetFile()
this.SetFile()
DatenEinlesen.SetFile()
noch was mit @classmethod
Und nun habe ich aufgegeben dies alleine zu lösen...

Lieben Gruß Frokuss
Benutzeravatar
pillmuncher
User
Beiträge: 1144
Registriert: Samstag 21. März 2009, 22:59
Wohnort: München

Donnerstag 6. Juni 2019, 01:12

Wenn du das offizielle Tutorial durcharbeitest, wirst du sehen, welche deiner Fehlannahmen darüber, wie Python funktioniert, zu den Fehlern geführt haben, über die du jetzt stolperst. Bitte halte meine Antwort nicht für unhöflich, aber ich halte es für wenig sinnvoll, hier nochmal dasselbe zu schreiben, was jemand bereits im Tutorial geschrieben hat. Hier der Link dazu: https://docs.python.org/3/tutorial/

Aber das noch: Du musst eine Methode über self aufrufen. Dazu muss die Methode aber ebenfalls so definiert sein, dass der erste Parameter self ist. Der Name self ist dabei übrigens rein konventionell und könnte auch this oder Peter sein, aber das macht niemand, weil es ziemlich unerwartet wäre. Vieles in Python beruht auf solchen Konventionen. Ein weiterer Grund, das Tutorial durchzuarbeiten, weil man dabei einige dieser Konventionen gezeigt bekommt.
In specifications, Murphy's Law supersedes Ohm's.
Frokuss
User
Beiträge: 16
Registriert: Mittwoch 5. Juni 2019, 20:35

Donnerstag 6. Juni 2019, 01:30

Jo danke. Mir war absolut nicht klar, dass man irgendwie alles importen muss. Dachte das wäre direkt mit der Klasse abgehandelt...

Code: Alles auswählen

from datenEinlesen import *
Jetzt noch nen paar andere Fehler debuggen.... (das ganze mit self und so)

Gruß Frokuss
Benutzeravatar
__blackjack__
User
Beiträge: 4214
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Donnerstag 6. Juni 2019, 01:41

@Frokuss: `pandas` wird üblicherweise als `pd` importiert, das solltest Du nicht nach `tab` umbenennen. So ausgerichtete Kommentare am Ende einer Zeile macht man auch nicht. Die Zeilen sind da auch ein bisschen sehr lang. 80 Zeichen sind immer noch an vielen Stellen eine mehr oder weniger feste Grenze die Probleme bereitet, wenn man sie überschreitet.

Namen schreibt man in Python klein_mit_unterstrichen. Ausnahmen: Konstanten (KOMPLETT_GROSS) und Klassen (MixedCase). Ein führender Unterstrich kennzeichnet Namen als Implementierungsdetail (und lokale Namen als „nicht verwendet“). Ein folgender Unterstrich wird verwendet wenn der Name sonst mit einem Schlüsselwort kollidiert und manchmal auch wenn er sonst mit einem eingebauten Wert kollidiert, damit das eben nicht passiert. Die Konvention von ein führender *und* folgender Unterstrich gibt es nicht. Da müsstest Du dokumentieren was das bedeuten soll, oder es besser lassen. Es gibt einige wenige Bibliotheken die das machen um eigene ”magische“ Namen einzuführen, das ist aber hier eindeutig nicht der Fall.

Namen mit doppelten führenden *und* folgenden Unterstrichen haben eine besondere, ”magische” Bedeutung und sollen nicht selbst erfunden werden. Die sind den Sprachentwicklern vorbehalten um damit Dinge zu benennen die ein Zusammenspiel aus Sprache/Syntax und Code zu ermöglichen den jeder schreiben kann. Also beispielsweise Operatorüberladung, Serialisierung, Konstruktoren, oder die Initialisierung von Objekten.

Eingerückt wird mit vier Leerzeichen pro Ebene. Nicht mehr, nicht weniger, und vor allem keine Tabulatorzeichen. Einrückung ist wichtig, und wenn das jeder macht wie er denkt, kann man nicht zusammenarbeiten. Auch Code in einem Forum posten gehört zur zusammenarbeit. Wenn jemand solchen Code ausprobieren möchte um bei der Fehlersuche zu helfen, muss er sonst nämlich entweder seine Editoreinstellungen ändern, oder den Code erst einmal umformatieren – und hoffen das dabei nichts kaputt geht oder eine andere Bedeutung bekommt.

Die Tabulatorzeichen machen dann auch gleich mal die ”schöne” Ausrichtung der Kommentare kaputt wenn man nicht Deine Editoreinstellungen für die Länge eines Tabulators verwendet. Ausgerichtete Kommentare machen auf Dauer auch mehr Arbeit, weil man da potentiell mehr Zeilen ändern muss um das wieder gerade zu rücken als die tatsächliche inhaltliche Code-Änderung ausmachen. So etwas ist ungünstig in Diffs.

Trennlinien mit Kommentaren sind auch unsinnig. Die verwendet keiner, dementsprechend störend wirken sie dann auch auf den Lesefluss.

Von den Importen wird keiner verwendet. Maximal `os` in auskommentiertem Code. Wenn man etwas nicht braucht, oder laut Kommentar gar noch nicht einmal weiss wofür es gut ist, importiert man es nicht.

Keine Sternchen-Importe machen. Die sind Böse™. Dann könnte man sich die Trennung in Module auch gleich sparen mit den negativen Konsequenzen die das hat.

Von `object` braucht man in Python 3 nicht mehr explizit erben, das passiert implizit wenn man keine Basisklasse angibt.

`DatenEinlesen` ist kein Klassenname. Klassen modellieren ”Dinge”, `DatenEinlesen` beschreibt aber eine Tätigkeit. Das wäre ein Name für eine Funktion oder Methode.

Die ganzen an `None` gebundenen Namen auf Klassenebene gehören mit 99,9%tiger Sicherheit nicht hin. In der Regel definiert man auf Klassenebene nur Konstanten und Methoden.

In der `__init__()`, aber spätestens nachdem die abgearbeitet wurde, sollten alle Attribute definiert sein. Es ist verwirrend wenn neue Attribute bei anderen Methodenaufrufen plötzlich entstehen können. Es ist für mich auch nicht nachvollziehbar was da überhaupt gemacht/versucht werden soll, mit all den doch recht ähnlichen Namen.

`file` ist etwas wo der Leser ein Dateiobjekt erwartet. Also zum Beispiel ein Objekt mit einer `close()`-Methode und `read()` und/oder `write()`. Keine Zeichenkette mit einem Datei*namen*.

Die `Set*()`-Methoden sehen für Python etwa komisch aus weil es fast triviale Setter-Methoden sind. Für so etwas hat Python Properties (`property()` als Dekorator). Wobei man wohl statt der Setter eher ein Getter-Property für `file_string` schreiben würde, weil die beiden Setter fast identischen Code enthalten. Wobei `file_string` ein komischer Name ist. Mir ist nicht klar warum das so heisst.

Properties sollte man auch in betracht ziehen damit `self.metas` und `self.separators` und `self.merkmale` nicht redundant sind, damit es nicht passieren kann, das die Werte dieser Attribute nicht zusammenpassen.

`os.path.join()` hat zumindest bis einschliesslich Python 3.6 kein `sep`-Argument.

Der Traceback passt nicht zum gezeigten Code. Zum einen ist die Gross-/Kleinschreibung in der Zeile in der die Ausnahme ausgelöst wird verschieden, zum anderen hat der gezeigte Quelltext ein `SetFile` auf Klassenebene definiert.

Auf Methoden des Objektes selbst greift man über `self` zu, was ja für das Objekt selbst steht. Und normale Methoden müssen bei der Definition dieses Argument auch als erstes in der Argumentliste stehen haben.

Falls Du da anfängst eine Klasse pro Modul zu definieren: Das macht man in Python nicht. Ein Modul ist dazu da mehrere Klassen und Funktionen zusammenzufassen. Das ein Modul nur eine Klasse enthält kann natürlich vorkommen, ist aber nicht die Regel.
“Give a man a fire and he's warm for a day, but set fire to him and he's warm for the rest of his life.”
— Terry Pratchett, Jingo
Frokuss
User
Beiträge: 16
Registriert: Mittwoch 5. Juni 2019, 20:35

Donnerstag 6. Juni 2019, 02:52

Das ist ja mal krass. Super lieb von dir :-) Danke! Ich weis gar nicht, wo ich anfangen soll... Zunächst einmal, ich habe noch nie was in Python gemacht - brauche es aber für die Vorlesung. Der Prof hat uns gestern (Dienstag) eine Projektaufgabe gegeben, die in unter 2 Wochen fertig sein soll.
Erstellen Sie für jeden der folgenden Schritte einer Datenanalyse mit Python Scikit-Learn einen „Programmierbaustein“, also ein Quelltextfragment, mit dem Sie statistische Daten verarbeiten können.Speichern Sie die Programmierbausteine in einem OrdnerProgrammierbausteine. Geben Sie dazujeweils auch die Anweisungen an, um die notwendigen Python-Module und Pakete zu importieren.
(a)Einlesen von Daten aus einer Datei: Gegeben sei eine CSV-Datei, die als Zellseparator entweder ein Komma, ein Semikolon oder einen Tabulator hat. Schreiben Sie die Befehle in eine Quelltextdatei
Das ist die erste von vier Teilaufgaben aus dem ersten von zwei Projekten...

Ein Grund, warum eventuell einiges nicht vorgekommen ist, könnte daran liegen, dass ich auch nicht den ganzen Quellcode gepostet habe, da dies auch nicht für den Fehler notwendig war. Aber hier hast du meinen aktuellen Code (mit einigen von dir angesprochenen DIngen bereits geändert :-D ):

Code: Alles auswählen

import os            #fuer das importieren
#import pandas as pa        #fuer die Tabellen
import numpy as np        #fuer KEINE AHNUNG
import re            #fuer regulaere Ausdruecke (gut dass es das gibt!)
#import csv            #Es gibt sogar eine CSV-Bib... Danke fuer die Info!

class DatenEinlesen(object):

    fileString = None
    _fileName_ = None
    _pathName_ = None
    metas = None
    seperator = None
    merkmale = None
    
    def __DirWithFile__(self, pathName, fileName):        #Setzt Dir und File zusammen --> private Methode
        l = len(pathName)            #zaehlt die Zeichen
        lastChar = pathName[l -1]        #speichert das letzte Zeichen des Pfades
    
        if lastChar != "/" and lastChar != "\\":
            return pathName + "/" + fileName
        else:
            return pathName + fileName
    
    def SetPath(self, path):
        self._pathName_ = path
        
        if self._fileName_ != None:
            self.fileString = self.__DirWithFile__(self._pathName_, self._fileName_)
   
    def SetFile(self, file):
        self._fileName_ = file
        if self._pathName_ != None:
            self.fileString = self.__DirWithFile__(self._pathName_, self._fileName_)

    def GetSep(self, txt, printy = False):
        #Optional in der ersten Zeile einer CSV! (z.B. sep=,)    https://de.wikipedia.org/wiki/CSV_(Dateiformat)#Besonderheiten_beim_Import
        reg1 = re.compile(",")
        reg2 = re.compile(";")
        reg3 = re.compile("\\t")        #\\t - wenn es wie bei einer normalen Sprache ist...

        m1 = re.search(reg1, txt)
        m2 = re.search(reg2, txt)
        m3 = re.search(reg3, txt)

        if "sep=" in txt:
            if printy:
                print("CSV-Vorgegebener Seperator ist:" + txt[len(txt)-1])
            return txt[len(txt)-1]
        if m1 and not m2 and not m3:
            return ","
        elif not m1 and m2 and not m3:
            return ";"
        elif not m1 and not m2 and m3:
            return "\t"
        else:
            if printy:
                print("konnte keinen eindeutigen Seperator finden:\n" + txt)
            return None    
            
    def GetRowNames(self, txt, sep = None):
        if sep == None:
            sep = self.GetSep(txt, True)        #Es soll eine Ausgabe geben!

        if sep == None:
            return None
        else:
            return txt.split(sep)

    def GetMetas(self, file):
        f = open(file)
    
        v1 = None
        v2 = None
        line = f.readline()

        v1 = self.GetSep(line, False)
        if "sep=" in line:
            line = f.readline()

        v2 = self.GetRowNames(line, v1)
        v2[len(v2) -1] = v2[len(v2) -1].replace("\n", "")
        f.close()
        
        return [v1, v2]
    
    def __init__(self, pathName, fileName, sep = None):        #sep = optionales Trennzeichen
        self.SetPath(pathName)
        self.SetFile(fileName)

        self.metas = self.GetMetas(self.fileString)
        self.seperator = self.metas[0]
        self.merkmale = self.metas[1]
        
        print("Metadaten generiert...")
        print(self.merkmale)

        #data_path = os.path.join("dataset", self.fileString)#, sep=self.metas[0]
        #tab.read(data_path)   

    def SetPathAndFile(self, path, file):
        self.SetPath(path)
        self.SetFile(file)

    def Hilfe():
        print("GetSep(txt, print = false)\t\t Liefert den Seperator einer CSV-Datei\n")
        print("GetRowNames(txt, sep = None)\t\t Liefert einen Array mit den Spalten-namen\n")
        print("GetMetas(fiel)\t\t Liefert einen Array mit [seperator; [rowNames]]\n")
        print("SetFile\n")
        print("SetPath\n")
        print("SetPathAndFile\n")
Und ja, ich habe die Unterstriche mehr oder minder bewusst verwendet:
_Protected_
__Private__

Aber, was für mich das größte Problem ist in Python, ist dieses "self" und wie mit dem Objekt umgegangen wird. Wenn ich eine Klasse habe, in dem dann Methoden sind, gehe ich davon aus, dass diese irgendwie auch im Object bekannt sind. Aber hier irgendwie nicht. Und aus dem Grund, verwende ich diesen * bei dem Import. Warum Python an dieser Stelle (in meinen Augen!) da einfach bescheuert ist, kann ich mir nicht erklären. Und da ich dafür auch nur knapp 2 Wochen Zeit habe, setze ich ein wenig auf Funktionalität ^^

Lieben Gruß Frokuss

PS: Werde versuchen in Zukunft das ein oder andere von deinem Post zu beherzigen :-) Danke nochmal!
Benutzeravatar
sparrow
User
Beiträge: 1360
Registriert: Freitag 17. April 2009, 10:28

Donnerstag 6. Juni 2019, 04:29

Du verwendest die Untetstriche vielleicht bewusst, aber wie __BlackJack__ sagte, du verwendest sie falsch. Und zwar konsequent.

Ich weiß ja nicht, was du studierst, aber sollte das mit Informatil zu tun haben, finde ich deine Resistenz, sich auf Merkmale einer Sprache einzulassen und Hinseise zum Code anzunehmen, leicht erschreckend.
Benutzeravatar
ThomasL
User
Beiträge: 771
Registriert: Montag 14. Mai 2018, 14:44
Wohnort: Kreis Unna NRW

Donnerstag 6. Juni 2019, 06:18

Mir stellt sich die Frage, wofür dieses umständliche Modul.
Dafür gibt es Pandas.
https://pandas.pydata.org/pandas-docs/s ... d_csv.html
Ich bin Pazifist und greife niemanden an, auch nicht mit Worten.
Für alle meine Code Beispiele gilt: "There is always a better way."
https://projecteuler.net/profile/Brotherluii.png
Sirius3
User
Beiträge: 10569
Registriert: Sonntag 21. Oktober 2012, 17:20

Donnerstag 6. Juni 2019, 07:09

@Frokuss: ich weiß ja nicht, wie Du Python lernst, aber Deine Interpretation der Sprache ist lustig. Es gibt kein _protected_ oder __privat__, per Konvention bedeutet _intern "nicht für die allgemeine Verwendung gedacht", und mehr braucht man auch nicht.
`__DirWithFile__` ist komplett überflüssig und in allgemeinen auch falsch. Das letzte Zeichen, oder das letzte Element einer Liste bekommt man mit negativen Indizes, pathname[-1], da muß man nicht extra die Länge bestimmen. Zum Arbeiten mit Pfaden gibt es pathlib und dort werden Pfade einfach per / zusammengesetzt:

Code: Alles auswählen

path = pathlib.Path('/irgend/ein/pfad/')
full_filename = path / filename
Dafür eine Methode zu definieren ist also nicht nur umständlich und fehleranfällig, sondern erschwert jedem Leser, was da wirklich passiert, weil er sich in Deine Logik einlesen muß, statt das Wissen der Standardbibliotheken nutzen zu können. Die Methode ist außerdem gar keine Methode und gehört nicht in die Klasse, das erkennt man daran, dass `self` gar nicht benutzt wird.

Es ist auch seltsam, dass man Pfad und Filename separat setzen kann. Da anscheinend sowieso nur eine Datei referenziert wird, ist so eine Trennung überflüssig und macht das Programm nur unnötig lang und schwer zu verstehen, weil jeder erwartet, Dateinamen inklusive Pfad angeben zu können.

Es ist nicht so einfach, aus csv-Dateien den Separator erraten zu können. Sicherer ist es, diesen explizit beim Laden anzugeben. Wenn Du es aber doch raten mußt, dann sind reguläre Ausdrücke zum Suchen eines einzelnen Zeichens unnötig kompliziert.
Die Prüfung "sep=" sieht sehr abenteuerlich aus. Wenn kein eindeutiger Separator gefunden wird, ist es dann sinnvoll None als Ergebnis zu liefern? Wie wird das weiterverarbeitet? Oft ist es ja so, dass None bedeutet, irgendeinen Defaultwert zu verwenden.
Da sollte irgendwo ein Hinweis sein, dass GetSep die erste Zeile erwartet. Statt überall ein `printy`-Argument anzugeben, benutzt man das logging-Modul.
Auf None prüft man üblicherweise mit `is`, das gilt aber nicht bei anderen Werten.
In `GetMetas`: das Zeile-Ende-Zeichen löscht man, bevor man die Zeile splittet. Dateien werden mit dem with-Statement geöffnet, das impliziert ein close().
Keine kryptischen Variablennamen verwenden, v1 und v2, gehts noch? Das eine ist ein Separater, das andere sind Spaltenüberschriften. Üblicherweise liest man auch gleich die ganze Datei, denn so mußt Du beim Lesen der eigentlichen Daten ja wieder prüfen, ob die erste Zeile ein sep= enthält.

`Hilfe` braucht man nicht, weil mit der `help`-Funktion von Python automatisch die Klassenstruktur in einen Hilfetext verwandelt wird.
Alle Klassenattribute sind keine.

Was bleibt ist das:

Code: Alles auswählen

import logging
logger = logging.getLogger(__name__)


def guess_seperator(line):
    #Optional in der ersten Zeile einer CSV! (z.B. sep=,)    https://de.wikipedia.org/wiki/CSV_(Dateiformat)#Besonderheiten_beim_Import
    if line.startswith("sep="):
        sep = line.strip()[-1]
        explicit = True
        logger.info("CSV-Vorgegebener Seperator ist: %s",  sep)
    else:
        explicit = False
        seps = set(c for c in [",", ";", "\t"] if c in line)
        if len(seps) == 1:
            sep = seps.pop()
        else:
            logging.info("konnte keinen eindeutigen Seperator finden:\n%s", line)
            sep = None
    return sep, explicit

class DatenEinlesen:
    # TODO: besseren Klassennamen finden
    def __init__(self, filename, sep=None):
        """
        Arguments:
            filename: csv-filename, optionally including path
            sep: separator character, if None try to guess
        """
        self.filename = filename
        self.read_header()
        print("Metadaten generiert...")
        print(f"Separator: {self.seperator}")
        print(f"Merkmale: {self.merkmale}")

    def read_header(self):
        with open(self.filename) as lines:
            header = next(lines)
            sep, explicit = guess_seperator(header)
            if explicit:
                # first line was sep=
                header = next(lines)
            self.seperator = sep
            if sep is not None:
                self.merkmale = header.strip().split(sep)
            else:
                # TODO: hier sollte etwas sinnvolles passieren
                self.merkmale = None
[/python]
Benutzeravatar
__blackjack__
User
Beiträge: 4214
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Donnerstag 6. Juni 2019, 10:19

@Frokuss: Wie ja schon gesagt wurde, ``private`` und ``protected`` gibt es in Python nicht, aber die Konvention das Namen mit *einem* führenden Unterstrich nicht zur öffentlichen API gehören und man solche Namen nur anfassen sollte, wenn man weiss was man da tut. Also in der Regel nur die Programmierer des jeweiligen Codes.

Python hat halt ein explizites Argument für das Objekt auf dem eine Methode aufgerufen wurde. Was ist daran ”bescheuert”? Das ist einfach und eindeutig. Bei Programmiersprachen mit einem impliziten ``this``, ``Self``, oder ähnlichem gibt es nicht selten Namenskonventionen um Attribute als Instanzattribute zu kennzeichnen oder die Konvention das ``this``/``Self``/… immer explizit hinzuschreiben, auch wenn es nicht zwingend nötig wäre. Ich fände es bescheuerter wenn sich die Bedeutung von einem Namen in einer Methode nur dadurch ändern würde das man eine Methode zu einer Klasse hinzufügt. Beispiel:

Code: Alles auswählen

def spam():
    print('Spaaaam!')


class Parrot:

    def frobnicate(self):
        spam()
Wenn man jetzt hier zu `Parrot` eine `spam()`-Methode hinzufügt, oder eine Klasse von `Parrot` ableitet und der eine `spam()`-Methode verpasst, fänd ich das extrem ungünstig wenn `frobnicate()` dann plötzlich nicht mehr die `spam()`-Funktion aufrufen würde, sondern stattdessen die neue Methode. Wenn dem so wäre kann man sich ganz leicht, überraschende und nicht so leicht zu findene Probleme einfangen. Das man in Python das Objekt selbst immer über `self` ansprechen muss ist einfach und eindeutig.

Was der ``*`` beim Import damit zu tun haben soll ist mir schleierhaft‽

Trennzeichen und Kopfzeile (Werte und/oder vorhanden sein oder nicht) bei CSV-Dateien sollte man immer auch explizit angeben können, weil das nicht immer sicher aus den Daten geraten werden kann.

Von dieser ``sep=``-Zeile mal abgesehen – die ich in der Praxis bisher noch nicht gesehen habe — hat das `csv`-Modul mit `Sniffer` schon ein wesentlich umfangreicheres Werkzeug um Trenn- und Quotezeichen zu raten, sowie das vorhandensein von einer Kopfzeile. Selbst wenn man das mit der ``sep=``-Zeile braucht, macht es Sinn nur dafür selbst Code zu schreiben und für den Rest den `Sniffer` zu verwenden.

Ich sehe bis jetzt auch noch nicht wirklich einen Sinn in einer Klasse. Das kann ja vielleicht noch kommen, aber ich wollte das auf jeden Fall erwähnen weil Python kein Java ist oder eine ähnliche Sprache wo man alles in Klassen presst, ob das nun Sinn macht oder nicht. Wenn etwas mit einer Funktion lösbar ist, dann schreibt man dafür auch einfach nur eine Funktion. Wenn eine Methode das `self`-Argument nicht verwendet, oder nur um ”Methoden” aufzurufen die das `self`-Argument auch nur dafür verwenden, dann ist es keine Methode und man muss a) begründen können, warum das in der Klasse steckt und nicht einfach eine Funktion ist, und b) sollte man es, wenn man solch eine Begründung hat, mit `staticmethod()` kennzeichnen/dekorieren, damit der Leser die “Methode“ leicht als Funktion erkennen kann, und das sich der Autor dessen auch bewusst ist, und diese Entscheidung wissentlich/aktiv getroffen hat.
“Give a man a fire and he's warm for a day, but set fire to him and he's warm for the rest of his life.”
— Terry Pratchett, Jingo
Frokuss
User
Beiträge: 16
Registriert: Mittwoch 5. Juni 2019, 20:35

Donnerstag 6. Juni 2019, 11:04

Okay, erst einmal vielen lieben Dank für die ganzen Infos. Ich glaube das überfordert mich dann doch ein wenig. Da die Frage im Raum stand, was ich studieren... nichts mit Informatik. Es ist ein Wirtschaftsingenieur, in dem wir mal einen Krus C++ hatten und sonst nichts mehr in diese Richtung. Was die Frage angeht, wie ich Python lerne - ihr habt ja mein aller erstes Script gesehen, ich habe gestern erst Python auf meinem System installiert ^^ Es ist ein wenig nach Try and Error Prinzip... Aber wie gesagt, noch nie etwas mit Python gemacht.

Was für mich auf jeden Fall neu ist, ist, dass man in Python Methoden ausserhalb der Klasse deklarieren kann. Das Script von Sirus hat mi auf jeden Fall weitergeholfen (nein, ich habe es nicht übernommen), wird mir aber als Inspiration für die Aufgabe dienen, die ich noch einmal erneut anfangen werde - da mir der ganze Code so einach gar nicht gefällt...

Lieben Gruß Frokuss

EDIT: Ahhh! Multiple Return-Werte. Das ist mir völlig neu. Das ist mal geil :-)
__deets__
User
Beiträge: 6395
Registriert: Mittwoch 14. Oktober 2015, 14:29

Donnerstag 6. Juni 2019, 11:31

Das sind dann keine Methoden, sondern Funktionen. Auch die beherrscht C++. Ausser Java kenne ich eigentlich keine Sprache, die einen ZWINGT, alles in Klassen zu stopfen.
Benutzeravatar
__blackjack__
User
Beiträge: 4214
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Donnerstag 6. Juni 2019, 11:45

@Frokuss: Methoden ausserhalb von Klassen sind Funktionen, und die gibt's in C++ doch auch‽

Multiple Rückgabewerte gibt's nicht. Man kann immer nur *einen* Wert mit einem ``return`` zurückgeben. Das kann natürlich auch eine Sequenz sein, wie ein Tupel oder eine Liste. Das machst Du doch in `GetMeta()` schon selbst – eine Liste mit zwei Elementen zurückgeben.

Üblicher ist in Python halt ein Tupel dafür zu verwenden, weil man Listen für Sequenzen verwendet wo jedes Element die gleiche Bedeutung hat, aber vom Prinzip her ist das ja das gleiche. Ich denke mal Du findest das ”unpacking” bei der Zuweisung so toll, das ist aber unabhängig davon wo der der Wert der da entpackt wird, her kommt. Und das funktioniert auch überall wo eine Zuweisung stattfindet (``a, b = pair``), also auch in ``for``-Schleifen. Beispiel: ``for a, b in pairs:``. Und es kann jedes iterierbare Objekt auf diese Weise entpackt werden, nicht nur Listen und Tupel. Beispielsweise ``x, y, z = map(float, line.split(','))`` wenn `line` eine Zeichenkette mit drei durch Kommas getrennte Zahlen sind.

@__deets__: Mir würde spontan noch Smalltalk einfallen. Und Io, das einen zwar nicht zu Klassen zwingt/zwingen kann (es gibt keine Klassen), wo es aber streng genommen keine Funktion geben kann, sondern nur Methoden. Beide Sprachen mag ich lieber als Java. 😎
“Give a man a fire and he's warm for a day, but set fire to him and he's warm for the rest of his life.”
— Terry Pratchett, Jingo
Benutzeravatar
__blackjack__
User
Beiträge: 4214
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Donnerstag 6. Juni 2019, 23:07

@Frokuss: Um das mit den Funktionen bei C++ noch mal zu demonstrieren und das „multiple Rückgabewerte“ auch C++ nicht sooo anders sind, habe ich die `guess_separator()`-Funktion von Sirius3 mal nach C++ übersetzt:

Code: Alles auswählen

#include <array>
#include <iostream>
#include <set>
#include <string>
#include <tuple>
#include <utility>

std::pair<char, bool> guessSeperator(const std::string &line)
{
    static const std::string sepPrefix("sep=");
    
    auto isExplicit = line.compare(0, sepPrefix.size(), sepPrefix) == 0;
    if (isExplicit) {
        return std::make_pair(line.at(sepPrefix.size()), isExplicit);
    } else {
        std::set<char> foundSeperators;
        std::array<char, 3> separatorCandidates {',', ';', '\t'};
        for (auto &s : separatorCandidates) {
            if (line.find(s) != std::string::npos) foundSeperators.insert(s);
        }
        if (foundSeperators.size() == 1) {
            return std::make_pair(*foundSeperators.cbegin(), isExplicit);
        }
    }
    return std::make_pair('?', isExplicit);
}

int main(int argc, char *args[])
{
    if (argc >= 2) {
        char separator;
        bool isExplicit;
        
        std::tie(separator, isExplicit) = guessSeperator(args[1]);
        std::cout << separator << '|' << isExplicit << std::endl;
    }
    return 0;
}
“Give a man a fire and he's warm for a day, but set fire to him and he's warm for the rest of his life.”
— Terry Pratchett, Jingo
__deets__
User
Beiträge: 6395
Registriert: Mittwoch 14. Oktober 2015, 14:29

Freitag 7. Juni 2019, 07:21

Das würde man aber eher std::tuple verwenden. Womit in C++17 dann sogar unpacking funktioniert. Mit auto.
Frokuss
User
Beiträge: 16
Registriert: Mittwoch 5. Juni 2019, 20:35

Freitag 7. Juni 2019, 22:27

Okay, man lernt nie aus, das habe ich in der Tat nicht gewusst ^^ Aber ich komme jetzt wenigstens etwas in Syntax rein. zumindestens das Problem hat sich schon mal reduziert :-D

Lieben Gruß Frokuss
Antworten