Klassen-Instanz mit Hilfe von Variable erstellen

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
fpmr
User
Beiträge: 7
Registriert: Donnerstag 14. Mai 2015, 14:41

Hi,
ich bin recht neu in Python und auch neu hier im Forum und habe eine Frage die ich mir nicht wie sonst mit Recherche im Internet erklären kann.....

Ich möchte mit Hilfe einer sich später durch eine Schleife ändernde Variable eine Instanz von einer Klasse erstellen, hier mal Beispielhaft der Code:

Code: Alles auswählen

class Testklasse():
    def __init__(self):
        self.variable = 3000

a = "box"

a = Testklasse()
bekomme jedoch dann wenn ich

Code: Alles auswählen

print(box.variable)
sage folgende Fehlermeldung:
Traceback (most recent call last):
File "C:/Users/Frederik/Desktop/test.py", line 10, in <module>
print(box.variable)
NameError: name 'box' is not defined
Wahrscheinlich ist die Lösung ganz einfach, ich komme jedoch nicht darauf und weiß nicht genau wonach ich suchen soll....

Vielen Dank für eure Hilfe!

LG
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Wenn man Werte dynamisch an Namen binden will, dann eignet sich ein Wörterbuch (dict) ganz gut. In welchem Zusammenhang benötigst du das denn?
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

Dynamisch Variablen zu erfinden ist fast immer eine schlechte Idee. Dein Anwendungsfall schreit nach einem Dictionary.

Code: Alles auswählen

class Foo:
    def __init__(self):
        self.bar = 42

key = 'box'
data = {}
data[key] = Foo()
data['thing'] = Foo()

for key, value in data.items():
    print(key)
    print(value.bar)
fpmr
User
Beiträge: 7
Registriert: Donnerstag 14. Mai 2015, 14:41

Danke für die schnellen Antworten!

Habe mich noch nicht besonders mit Dictionarys beschäftigt, werde das aber dann mal tun!

Ich will mit Hilfe einer Schleife eine Textdatei auslesen in der jeweils eine Zeile einen Wert darstellt, immer aus vier Zeilen soll dann eine Instanz entstehen, dessen Name der Wert der ersten Zeile im Block ist! Zusätzlich soll dann noch eine Liste mit den erstellten Instanzen erstellt werden!
Davon abgesehen, dass die Instanzen mit mir unverständlichen Namensräumen belegt werden sodass ich sie nicht abrufen kann klappt das laut der Kontrollausgabe ganz gut, ich poste mal meinen Code:

Code: Alles auswählen

#Fuktion um Daten aus der attributes.txt auszulesen
#Als Variable wird der Pfad der attributes.txt übergeben
def fetch_data(attributespath=""): 

    class Slide: #Objektklasse für die einzelnen Slides
        def __init__(self):
            self.dateiname = "Name" #Name des Slides
            self.duration = "Dauer" #Anzeigedauer des Slides
            self.start = "Startzeit" #Startzeitpunkt der Slide-Anzeige
            self.stop = "Stopzeit" #Endzeitpunkt des Slide-Anzeige

    slides = [] #Definiton für die Liste der Slides

    # Öffnen attributes.txt
    attributes = open(attributespath,"r")

    x = "test" #Kontroll-Variable für das Auslesen der attributes.txt

    #Schleife welche die Datei bis zum Ende ausließt
    #und die Datensätze in Objekte schreibt
    while (x != ""): 
        #Lesevorgang
        x = name = dateiname = attributes.readline()
        duration = attributes.readline()
        start = attributes.readline()
        stop = attributes.readline()

       
        #Stop-Anweisung für den Fall, das keine weiteren Datensätze vorliegen
        if (x==""):
            break

        #Zeilenumbrüche aus der attributes.txt werden entfernt
        name = name.replace("\n","")
        dateiname = dateiname.replace("\n","")
        duration = duration.replace("\n","")
        start = start.replace("\n","")
        stop = stop.replace("\n","")

        #Objektkinstanz für Slides wird definiert
        name = Slide()
        name.dateiname = dateiname
        name.duration = duration
        name.start = start
        name.stop = stop

        #Slide-Instanz wird der Liste angefügt
        slides.append(name)

        #Kontrollausgabe der Daten (wird in der finalen Version verschwinden)
        print(name)
        print(name.dateiname)
        print(name.duration)
        print(name.start)
        print(name.stop)

    #Kontrollausgabe der Slides-Liste
    print(slides)

    #Attributes.txt wird geschlossen
    attributes.close()

LG
BlackJack

@fpmr: Der Kommentar über der Funktion wäre als DocString besser aufgehoben. Die meisten Kommentare sind total nutzlos weil sie dem Leser nichts verraten was nicht schon als Code sowieso schon dort steht.

Warum wird die Klasse *in* der Funktion definiert? Das erstellt bei jedem Funktionsaufruf eine neue Klasse.

Statt die Attribute mit Platzhalterwerten zu füllen sollte man die gleich in der `__init__()` an die endgültigen Werte binden.

Die Mischung von englischen und deutschen Bezeichnern ist unschön.

`x` ist ein schlechter Name und ausserdem überflüssig. Der Wert wird ja auch noch an *zwei* andere Namen gebunden (wovon einer nie wirklich benutzt wird!). Die ``while``-Bedingung ist unsinnig weil die *immer* wahr ist, da in einem anderen Fall die Schleife vor der nächsten Prüfung durch ein ``break`` verlassen wird. Die Vorbelegung mit einem Unsinnswert damit der erste Test nicht fehlschlägt ist sehr unschön.

Die Klammern bei den Bedingungen gehören da nicht hin.

Warum heisst ein `Slide`-Exemplar `name`? Und nicht beispielsweise `slide`.

Dateien kann man zusammen mit der ``with``-Anweisung öffnen, dann werden sie beim Verlassen des ``with``-Blocks, egal aus welchen Gründen, garantiert geschlossen.

Die Attribute von `Slide` scheinen redundant zu sein, denn aus Start- und Endzeit ergibt sich die Anzeigedauer. Oder aus Startzeit und Anzeigedauer die Endzeit. Oder aus Anzeigedauer und Endzeit die Startzeit. Alles drei muss man jedenfalls nicht speichern. Ich würde es auf jeden Fall auf Konsistenz prüfen damit man keine kaputten Daten verarbeitet. Dann müsste man die Daten in die tatsächlich passenden Datentypen umwandeln um damit rechnen zu können.

Die ``while``-Schleife sieht ein wenig unelegant aus. Man könnte da mit `iter()` oder einem Generatorausdruck der die Zeilenenden entfernt und `zip()` leicht eine ``for``-Schleife draus machen.
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

@fpmr
Dass ein Datensatz aus jeweils 4 Zeilen einer Datei besteht ist IMHO keine so gute Idee. Die Daten, die zusammengehören (also ein Datensatz), sollten auch zusammen abgelegt sein. Wenn sich in Deiner Datei auch nur irgendwo etwas verschiebt, stimmt alles nicht mehr. Schau' Dir mal das json oder das csv Modul an. Damit kannst Du recht einfach Daten aus Textdateien einlesen und wieder ablegen.

Mit json könnte das so aussehen:

Code: Alles auswählen

In [28]: import json

In [29]: slide = {'FILENAME': 'filename', 'START': (12, 0), 'STOP': (12, 15)}

In [30]: with open('test.txt', 'w') as f:
    json.dump(slide, f)
   ....:     

In [31]: !cat test.txt
{"START": [12, 0], "STOP": [12, 15], "FILENAME": "filename"}
mutetella
Zuletzt geändert von mutetella am Freitag 15. Mai 2015, 17:42, insgesamt 1-mal geändert.
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
fpmr
User
Beiträge: 7
Registriert: Donnerstag 14. Mai 2015, 14:41

@BlackJack: Danke für die ausführliche Antwort!

Die Kommentare hatte ich für einen Kumpel eingefügt, der sich anschauen wollte was da passiert, aber noch weniger Ahnung von Python hat als ich!

Das die Klasse in der Funktion definiert ist ist sinnlos, da hast du recht, ich werde das ändern.

Ich hatte die Werte zuerst beim erstellen der Instanz übergeben, hatte das jedoch geändert, da ich bevor ich fest gestellt habe, dass die Instanzen einfach den Falschen Namen bekommen dachte, das dort der Fehler lag.

Das mit den Bezeichnungen ist klar, wird sich bestimmt noch geben wenn ich ein bisschen mehr Übung habe!

Den Break habe ich eingefügt, da die Schleife sonst noch einmal "leer" durchläuft! Die Klammer können weg, hatte ich übersehen!

Über die Benennung der Variablen kann man natürlich streiten, ich hatte sie jetzt einfach mal so benannt.....

Über "iter()" und "zip()" werde ich mich mal informieren!


Ich stehe jedoch trotz allem vor dem Problem, dass die Instanzen einen mir unerklärlichen namen bekommen wenn ich sie mit Hilfe einer Variable erstelle, nämlich etwas in der Art...:
<__main__.fetch_data.<locals>.Slide object at 0x03030CF0>

LG
BlackJack

@fpmr: Das ist nicht der Name des Objekts sondern die Zeichenkettendarstellung mittels `repr()`-Funktion — in diesem Fall eine Standarddarstellung die den Typen und die ID des Objekts beinhaltet. Ein Objekt kennt seine(n) Namen nicht, davon kann es gar keinen oder beliebig viele geben, Deine Objekte sind bis auf das letzte an keine Namen gebunden sondern stecken als Elemente in einer Liste.

Das ``break`` ist ja in Ordnung, die Bedingung beim ``while`` macht keinen Sinn und damit `x` auch nicht, denn für's ``break`` kann man ja `dateiname` prüfen ob's leer ist. Beim ``while`` kann man ganz einfach Konstant `True` als ”Bedingung” schreiben.
fpmr
User
Beiträge: 7
Registriert: Donnerstag 14. Mai 2015, 14:41

@BlackJack: Ah danke, das hilft mir sehr weiter!

X kann ich ja dann komplett rausnehmen!

Die Liste kann ich ja problemlos in ein anderes Programm importieren, habe dann allerdings Probleme die Instanzen aufzurufen. Kann ich denen einen eigenen Namen zuweisen, dass ich im Schema : modul.<Instanzname>.Variable(z.B. Start) aus einem anderen Programm darauf zugreifen kann?

LG
BlackJack

@fpmr: Das klingt ja noch gruseliger als einfach nur dynamisch Variablennamen erzeugen zu wollen. Steck die in ein Wörterbuch (`dict`) das von der Funktion zurückgegeben wird oder vielleicht ein `collections.OrderedDict` falls die Reihenfolge auch eine Rolle spielt.
fpmr
User
Beiträge: 7
Registriert: Donnerstag 14. Mai 2015, 14:41

@BlackJack : Das kann ich machen, ich verstehe allerdings nicht wie ich die Klassen dann in nem anderen Skript, dass auch die Funktion ausführt dann weiter benutzen kann... Das Wörterbuch hilft mir da ja nicht besonders, sonst könnte ich die Werte ja auch einfach so dahin übernehmen und auf die Klasse verzichten oder nicht?

LG
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

@fpmr
Wenn Du die slides aus dem einen Modul in einem anderen verwenden möchtest, dann könnte man das so lösen:

Modul 'slide.py'

Code: Alles auswählen

class Slide(object):
    def __init__(self, filename, duration, start, stop):
        self.filename = filename
        self.start = start
        self.stop = stop

def get_slides():
    pass
Modul 'foo.py'

Code: Alles auswählen

import slide

slides = slide.get_slides()
Innerhalb `slide.get_slides()` muss dann natürlich die Schleife zur Erstellung der slides stehen, die dann zurückgegeben werden.

mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
BlackJack

@fpmr: Welche Klassen? Du hast da nur eine Klasse. Ich denke Du meinst Exemplare. Nochmal: stecke die in eine geeignete Datenstruktur und gib diese dann als Rückgabewert zum Aufrufer zurück. Wenn Du sowohl die Reihenfolge der Daten aus der Datei erhalten möchtest als auch über den Dateinamen auf den jeweiligen Datensatz zugreifen können möchtest, dann ist die Datenstruktur ein `collections.OderedDict`. Wobei: Musst Du denn überhaupt über den Dateinamen auf Datensätze zugreifen oder reicht es die sequenziell abzuarbeiten?
fpmr
User
Beiträge: 7
Registriert: Donnerstag 14. Mai 2015, 14:41

@mutetella: Genauso hatte ich mir das ursprünglich gedacht! Habe mal einige Änderungen vorgenommen, nachdem hier ja einige Tipps gegeben wurden!

Der Code um die Instanzen zu erstellen ist jetzt folgender:

Code: Alles auswählen

class Slide(): #Objektklasse für die einzelnen Slides
    def __init__(self, filename, duration, start, stop):
        self.filename = filename #Name des Slides
        self.duration = duration #Anzeigedauer des Slides
        self.start = start #Startzeitpunkt der Slide-Anzeige
        self.stop = stop #Endzeitpunkt des Slide-Anzeige


def fetch_data(attributespath=""): 

    # Öffnen attributes.txt
    attributes = open(attributespath,"r")


    #Schleife welche die Datei bis zum Ende ausließt
    #und die Datensätze in Objekte schreibt
    while True: 
        #Lesevorgang
        filename = attributes.readline()
        duration = attributes.readline()
        start = attributes.readline()
        stop = attributes.readline()

       
        #Stop-Anweisung für den Fall, das keine weiteren Datensätze vorliegen
        if (filename==""):
            break

        #Zeilenumbrüche aus der attributes.txt werden entfernt
        filename = filename.replace("\n","")
        duration = duration.replace("\n","")
        start = start.replace("\n","")
        stop = stop.replace("\n","")

        #Objektkinstanz für Slides wird definiert
        filename = Slide(filename, duration, start, stop)


    #Attributes.txt wird geschlossen
    attributes.close()
das Skript welches ihn ausführt, ist:

Code: Alles auswählen

import slides

slides = slides.fetch_data("Y:/Promobox/Software/Python3/Slideshow_test/resources/attributes.txt")


print (slides)
Als Ausgabe erhalte ich jedoch lediglich "None"


@BlackJack: Du hast Recht ich meinte nicht Klassen sondern Instanzen(Exemplare?)!

Werde jetzt das ganze mal wie BlackJack sagt und wie /me beschrieben hat in ein Wörterbuch einbauen! Ich glaube ich habe den Zweck von Klassen noch nicht ganz geschnallt, ich dachte ich könnte mit davon erstellten Instanzen direkt arbeiten.....
Danke für die ganze Hilfe!
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

fpmr hat geschrieben:Als Ausgabe erhalte ich jedoch lediglich "None"
Warum hättest du etwas anderes als None erwartet? In fetch_data hast du kein return um Daten zurückzugeben. Du sammelst die Daten ja dort nicht einmal in einer Liste. Eine Funktion die kein explizites return hat gibt implizit None zurück.
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@fpmr: ist das jetzt ein Name oder ein Dateiname? Irgendwie sind sich da Kommentar und Attributname nicht einig. Ein Default-Wert eines Funktionsarguments, das einen Fehler produziert, macht keinen Sinn. Wenn das Argument zwingend ist, darf es keinen Default-Wert haben. Der attributespath ist auch kein Pfad sondern ein Dateiname. Dateien mit with zu öffnen, wurde Dir schon gesagt. Es ist unüblich in Python, Zeilen mit readline zu lesen, da eine for-Schleife einfacher, vielseitiger und verständlicher ist. Die Klammern bei if sind immer noch überflüssig. Statt replace nimmt man üblicherweise rstrip. Du wolltest ursprünglich alles in eine Liste packen und zurückgeben:

Code: Alles auswählen

def fetch_data(attributes_filename):
    with open(attributes_filename) as attributes:
        return [Slide(*map(str.rstrip, slide)) for slide in zip(*[attributes] * 4)]
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

fpmr hat geschrieben:Ich glaube ich habe den Zweck von Klassen noch nicht ganz geschnallt, ich dachte ich könnte mit davon erstellten Instanzen direkt arbeiten.....
Um Daten zu transportieren (übergeben) muss man diese nicht unbedingt in eine Klasse stecken. In Deinem Fall eignet sich eine Klasse aber zum Beispiel, um das zu realisieren, was BlackJack schon angesprochen hat:

Code: Alles auswählen

class Slide(object):
    def __init__(self, filename, start, stop):
        self.filename = filename
        self.start = start
        self.stop = stop
        self._duration = None

    @property
    def duration(self):
        return self.stop - self.start

Code: Alles auswählen

>>> s = Slide('my_file', 12, 13)
>>> s.duration
1
Und am Beispiel von Sirius3 siehst Du auch, dass Du vielleicht die Form, wie Du Deine Daten vorliegen hast, überdenken solltest... ;-)

mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
Antworten