Klasseninstanzen über Schleife erzeugen

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.
Tinker232
User
Beiträge: 50
Registriert: Mittwoch 25. Juli 2018, 13:45

Hallo Leute,

ich habe ein GUI für einen Sensorlogger entwickelt. Über eine .xml kann der Nutzer einige Parameter wie: Sensorname, Einheit etc. verändern.
Das XML wird vor Start der GUI entsprechend geladen. Für die Sensoren habe ich nun eine Klasse erstellt Neuer_Port():
Nun möchte ich nachdem die XML erfolgreich geladen ist, dass für eine Anzahl x entsprechende Instanzen der Klasse Neuer_Port erstellt werden.
Thereotisch gesehen stelle ich mir das so vor:

Code: Alles auswählen

class Neuer_Port():
      def __init__(Zahl, Name, Einheit):
              self.zahl = Zahl
              self.name = Name
              self.einheit = Einheit
              
def lade_xml():
        baum = ElementTree.parse('Einstellungen.xml')
        wurzel = baum.getroot()
        for i in range (1, 8):
              port_[ i ] = Neuer_Port(wurzel[ i ][1].text, wurzel[ i ][2].text, wurzel[ i ][3].text)
Leider funktioniert das natürlich so nicht und vermutlich macht man das auch nicht mit einer for-Schleife... Könnt ihr mir zeigen wie man es richtig macht?! :shock: :idea: :wink:
LG Tina
__deets__
User
Beiträge: 14494
Registriert: Mittwoch 14. Oktober 2015, 14:29

Doch, das macht man in mit einer for-schleife. Nur musst du halt das Ergebnis auch irgendwo ablegen, und "port_" gibt's ja nunmal nicht. Stattdessen kannst du zB eine Liste "ports" anlegen, und einen neuen port daran "append"en.

Und statt einer hart-kodierten Anzahl von i benutzt man natuerlich die Moeglichkeit, ueber die Kinderelement zu iterieren, die ElementTree schon beherrscht. Sonst ist die Konfigurierbarkeit ja irgendwie sinnlos.

(ungetestet)

Code: Alles auswählen

for i, node in enumerate(root.children, start=1):
      ...
Benutzeravatar
__blackjack__
User
Beiträge: 13006
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Wobei ich da dringend von den Indexzugriffen und auch vor dem einfach so drüber iterieren abraten würde und explizit angeben würde wie die Tags heissen, die da verwendet werden sollen. Das ist sonst alles ein bisschen magisch und fragil.

Warum heisst das `Neuer_Port`? Gibt's auch `Alter_Port`? Und ist `zahl` wirklich der treffendste Bezeichner für den Wert?
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Sirius3
User
Beiträge: 17712
Registriert: Sonntag 21. Oktober 2012, 17:20

@Tinker232: da Du nicht verraten hast, wie das XML wirklich aussieht, kann man auch nicht sagen, wie der Code zum Lesen richtig aussehen sollte, aber irgendwie so:

Code: Alles auswählen

def lade_xml(filename):
    baum = ElementTree.parse(filename)
    ports = []
    for port in baum.findall('port'):
        ports.append(NeuerPort(port.findtext('zahl'), port.findtext('name'), port.findtext('einheit')))
    return ports
Wobei `port_` ein schlechter Name für eine Liste ist, wegen des _ und weil Listen meist im plural benannt werden. port_ kommt aus dem Nichts und ist schon mit Werten vorbelegt? Das macht man nicht, sondern erzeugt einfach eine neue Liste. Variablen schreibt man generell komplett klein, Klassen in CamelCase. Die Klasse könnte hier auch ein NamedTuple sein.
Tinker232
User
Beiträge: 50
Registriert: Mittwoch 25. Juli 2018, 13:45

Hallo zusammen,

meine XML sieht ungefähr so aus:

Code: Alles auswählen

<einstellungen>
	<allgemein>
		...bla...
	</allgemein>
	<sensoren>
		<sensor>
			<id typ="int">1</id>
			<datei typ="str">Strom</datei>
			<einheit typ="str">A</einheit>
		</sensor>
		<sensor>
			<id typ="int">2</id>
			<datei typ="str">Leitwert</datei>
			<einheit typ="str">X</einheit>
		</sensor>
		<sensor>
			<id typ="int">3</id>
			<datei typ="str">Temp</datei>
			<einheit typ="str">F</einheit>
		</sensor>
	</sensoren>
<einstellungen>
Ich möchte nun das der Benutzer die Werte der Sensoren anpassen kann bzw. auch z.B. einen weiteren Sensor hinzufügen kann. Beim Laden der XML soll dann für jeden Sensor eine enstprechende Instanz von NeuerSensor angelegt werden.
Irgendwie müssten die angelegten Instanzen ja dann durchnummeriert oder eventuell mit den IDs aus der XML verknüpft werden?
Die For-Schleife soll dann so lange durchlaufen bis alle Sensoren initialisiert sind, bzw. angelegt sind. :-)
Sirius3
User
Beiträge: 17712
Registriert: Sonntag 21. Oktober 2012, 17:20

@Tinker232: dann passt ja meine Lösung, bis auf die Tag-Namen. Übrigens, falls Du was am Format ändern kannst, wirf die ganzen typ-Attribute raus. Das Programm, das die Datei verarbeitet, muß sowieso wissen, welchen Typ die Elemente haben, und kann sich nicht darauf verlassen, was der Schreiber für eine Typ-Attribut geschrieben hat. Im besten Fall sind sie also überflüssig, sonst führen sie zu Inkonsistenzen, die noch extra aufgelöst werden müssen.
__deets__
User
Beiträge: 14494
Registriert: Mittwoch 14. Oktober 2015, 14:29

@Tinker232: du musst doch keine IDs vorher anlegen, in der Hoffnung, die dann zuordnen zu koennen. Das id-Tag wuerde ich komplett rauswerfen (solange es nur eine Nummer ist), und einfach in einer for-Schleife die Sensor-Objekte anlegen.

Code: Alles auswählen

from collections import namedtuple
from xml.etree import ElementTree as et

class Sensor(namedtuple("Sensor", "datei einheit")):

    @classmethod
    def from_xml(cls, tag):
        return Sensor(tag.find("datei").text, tag.find("einheit").text)


DATA = """<einstellungen>
	<allgemein>
		...bla...
	</allgemein>
	<sensoren>
		<sensor>
			<datei>Strom</datei>
			<einheit>A</einheit>
		</sensor>
		<sensor>
			<datei>Leitwert</datei>
			<einheit>X</einheit>
		</sensor>
		<sensor>
			<datei>Temp</datei>
			<einheit>F</einheit>
		</sensor>
	</sensoren>
</einstellungen>"""

def main():
    doc = et.fromstring(DATA)
    sensors = [Sensor.from_xml(sensor_definition) for sensor_definition in doc.iter("sensor")]
    print(sensors)


if __name__ == '__main__':
    main()
Tinker232
User
Beiträge: 50
Registriert: Mittwoch 25. Juli 2018, 13:45

okkk... kapiere ich noch nicht so ganz. Ich möchte später auf jeden einzelnen Sensor zugreifen können, also z.B. print(sensor_1.wert), print(sensor_2.wert) wie erstellt denn die for schleife die einzelnen Instanzen mit welchen Instanznamen??
LG
Sirius3
User
Beiträge: 17712
Registriert: Sonntag 21. Oktober 2012, 17:20

@Tinker232: gar nicht. Es gibt keine Instanznamen. Das einzige was es gibt, sind Variablennamen, und das ist der Name, den man bei der for-Schleife angibt:

Code: Alles auswählen

for sensor in sensors:
    print(sensor.wert)
Tinker232
User
Beiträge: 50
Registriert: Mittwoch 25. Juli 2018, 13:45

Okay, aber ich hätte dann doch keine Unterscheidung zwischen dem sensor.wert des Sensors 1 und des sensor.wert des zweiten Sensors... Hoffe du verstehst meinen Denkknoten :D
Benutzeravatar
snafu
User
Beiträge: 6732
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Na, der erste Sensor ist halt das erste Element aus der Liste. Da die Zählung bei 0 beginnt, musst du gedanklich immer eins drauf rechnen. Falls ein Sensor in Wirklichkeit mehr Daten im XML hat als nur den Wert, dann speicher besser den kompletten Sensor in der Liste und greife darauf zu.
__deets__
User
Beiträge: 14494
Registriert: Mittwoch 14. Oktober 2015, 14:29

Wo ist denn der substantielle Unterschied zwischen "self.sensor_1" und "self.sensors[0]"?

Entweder dein Programm kann per Konfigurationsdatei diverse Sensoren anlegen. Da ist es ja voellig legitim zB einen Namen im XML zu vergeben, damit man weiss, dass es sich dabei um die Temperatur des Spuelkastens handelt und das zB in der UI dann per Label mitteilt. Aber du treibst alle generierung von UI Elementen etc. basierend auf dynamischen Datenstrukturen.

Oder du hast eine konkrete Erwartung an 3 verbaute Sensoren fuer deinen Vogelkaefig, und dann brauchst du auch keine Konfiguration, und kannst "self.schwingstangen_temperatur" schreiben.

Einen Zwischenweg, wo du eine Konfiguration hast, die einen so hohen Freiheitsgrad hat, wie du es oben dargestellt hast, aber dann irgendwie der Code doch wieder konkrete Namen kennt - das geht halt nicht.

Ein Zwischenschritt waere, eine Konfiguration anzulegen, in der konkrete Sensoren benannt sind. Das wuerde ich dann aber auch sehr deutlich machen (und eigentlich auch nicht ueber XML), weil du zB einstellen willst, das der Alarm fuer Susi bei 20 Grad Schwingstangen-Temperatur losgeht, und bei Hansi erst bei 25 Grad. Weil die das halt unterschiedlich moegen.

Dann saehe aber eine Konfiguration auch ganz anders aus, als zB als INI-Datei

Code: Alles auswählen

[schwingstangensensor]
warn_level=25
Tinker232
User
Beiträge: 50
Registriert: Mittwoch 25. Juli 2018, 13:45

Sorry, ich hab mich vermutlich falsch ausgedrückt.
Der Benutzer kann in die XML soviele Sensoren eingeben wie er will. Die XML wird eingelesen und erstellt für jeden Sensor dann z.B. eine Instanz eines namedtuple names Sensor mit den Elementen name, wert, einheit.... sodass ich später jeweils auf die einzelnen Werte zugreifen kann.


Am besten wäre es wenn nun bei der Instanzierung des Tupels Sensor der Wert "name" aus der XML als Instanzenname genommen wird, sodass ich später ganz einfach auf sensor1.wert darauf zugreifen kann...
Muss das unbedingt mit einer Liste dann gemacht werden in der for-Schleife oder kann die for-Schleife nicht einfach die Instanzen des Tupels immer so erzeugen?
Sorry Leute für meine bescheuerten Fragen :oops:

Code: Alles auswählen

NeuerSensor = collections.namedtuple("Sensor", ["name", "wert", "einheit"])
XML:

Code: Alles auswählen

<einstellungen>
        <sensor>
        	<name>Sensor1</name>
        	<wert>0</wert>
        	<einheit>A</Einheit>
        </sensor>
        <sensor>
        	<name>Sensor2</name>
        	<wert>0</wert>
        	<einheit>C</Einheit>
        </sensor>
        <sensor>
        	<name>Sensor3</name>
        	<wert>0</wert>
        	<einheit>cm</Einheit>
        </sensor>
</einstellungen>
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

Speichere Deine Sensor-namedtuples doch einfach in einem Dictionary mit den Namen als keys.
__deets__
User
Beiträge: 14494
Registriert: Mittwoch 14. Oktober 2015, 14:29

Wobei das ja das Problem auch nicht loest. Denn die Namen muessen ja auch irgendwo herkommen. Ob du nun "self.sensoren['schwingstangentemperatur']" oder self.sensor_schwingstangentemperatur schreibst ist prinzipiell egal - beides ist Code, der eine konkrete Annahme ueber einen Namen trifft.

Dein gesamter Code *muss* mit einer Liste klarkommen. Was du aber natuerlich machen kannst, ist einen konkreten Sensor uebergeben an ein Objekt, das dessen Betrieb & Darstellung regelt. Mal ganz grob skizziert:

Code: Alles auswählen

class SensorDisplay:

      def __init__(self, sensor, parent):
            self.setup_gui(parent)
            self._sensor = sensor
            
      def read_and_update(self):
             self._sensor.read_value()
             self._label.configure(text="{}".format(self._sensor.value))

... 
class Application:
    def setup(self):
      ...
      sensors = read_sensors_from_xml(config_file)
      root = setup_gui()
      self._sensor_displays = [SensorDisplay(sensor, root) for sensor in sensors]
      

    def timer_callback(self):
          for sd in self._sensor_displays:
                sd.read_and_update()
Sirius3
User
Beiträge: 17712
Registriert: Sonntag 21. Oktober 2012, 17:20

@Tinker232: da wir nicht wissen, was Du denn mit den Sensor-Werten machen willst, kann man auch schlecht Tipps geben. So wie Du es jetzt schreibst, schein ein Wörterbuch eine mögliche Lösung sin.


@__deets__: in anderen Fragen scheint Tinker232 Qt zu benutzen. Wie kommst Du jetzt auf Fragmente die wie Tkinter aussehen? Ich bin verwirrt.
Benutzeravatar
sls
User
Beiträge: 480
Registriert: Mittwoch 13. Mai 2015, 23:52
Wohnort: Country country = new Zealand();

__deets__ hat geschrieben: Montag 1. Oktober 2018, 16:46 self._sensor_displays = [SensorDisplay(sensor, root) for sensor in sensors]
Hätte hier statt der List-Comprehension ein list(map(SensorDisplay, sensors)) irgendwelche Vorteile ?

Was ich mich noch Frage: wer verwendet heutzutage noch XML?
When we say computer, we mean the electronic computer.
Sirius3
User
Beiträge: 17712
Registriert: Sonntag 21. Oktober 2012, 17:20

Ob man map oder LC benutzt, ist reine Geschmacksache (Wer verwendet heutzutage noch map?).

XML ist portabel und gut lesbar, vor allem bei komplexeren Strukturen. Jede Alternative hat zudem andere Vor- und Nachteile.
Benutzeravatar
__blackjack__
User
Beiträge: 13006
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Sirius3: Wobei „gut lesbar“ davon abhängt ob man es so gestaltet oder nicht. Es gibt auch einen ganzen Haufen XML-Anwendungen die nicht für Menschen alles mögliche, aber nicht gut lesbar sind. :-)

Ich verwende heutzutage noch `map()`. Allerdings eher nicht wenn ich eine Liste brauche, also wenn der nächstes Schritt dann wie hier der `list()`-Aufruf wäre.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Benutzeravatar
sls
User
Beiträge: 480
Registriert: Mittwoch 13. Mai 2015, 23:52
Wohnort: Country country = new Zealand();

Ich war nur etwas verwundert denn jede XML-Konfiguration die ich bisher sah war strukturell der reinste Horror und im Zusammenhang mit Sicherheitslücken von Webservern fiel dabei immer wieder XML. Aber gut, dann ist auch das alles eine Frage von "wie man es verwendet".

Mit higher order functions wie map, filter, reduce, hatte ich bis dato noch nicht so intensiv gearbeitet, daher fragte ich mich ob da ein grundlegener Vor- oder Nachteil bestünde. Danke für die Aufklärung.
When we say computer, we mean the electronic computer.
Antworten