Zugriff auf dict von class aus gesehen

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.
Holzbein
User
Beiträge: 9
Registriert: Freitag 16. Dezember 2022, 13:53

Hallo

Ich habe ein Main.py Programm. Dort wird ein Dict definiert. Das soll dann durch einen Rückgabewert einer class Funktion erweitert werden.
Ändere ich im ursprünglichen dict nur einen bestimmten Wert, dann sehe ich später in einer (anderen) class Funktion diese Änderung.
Ändere ich das ursprüngliche dict, indem ich einen ganzen Zweig austausche (gleiche Struktur, viele geänderte Werte), dann wir dies in einer class Funktion nicht sichtbar.

Dieses Austauschen sollte eben mit dem Rückgabewert (dict) aus einen bestimmten class Funktion geschehen.

Vielleicht kann man dieses Problem mit dem Beispiel erkennen.

Main.py

Code: Alles auswählen

#!/usr/bin/env python
# coding: utf8

from collections import defaultdict
import test_class as sensors
import copy

SENSOR_config_array=defaultdict(dict)
SENSOR_config_array["Sensors"]=defaultdict(dict)
SENSOR_config_array["Sensors"]["actualVal"]=3
SENSOR_config_array["Sensors"]["maxVal"]=10


A10_sensors_class = sensors.deal_with_sensorData(SENSOR_config_array)
print ("original SENSOR_config_array:")
print (SENSOR_config_array)
print()

ret=A10_sensors_class.make_DataString()	## return "maxVal" should change to 20
print ("1. ret :")
print (ret)								## OK
print()

SENSOR_config_array["Sensors"]["maxVal"]=20 ## "maxVal" set to 20
ret=A10_sensors_class.make_DataString()	## return "maxVal" should =40
print ("2. ret :")
print (ret)								## OK
SENSOR_config_array["Sensors"]=ret		## "maxVal" set to (returned)40
print ("2. SENSOR_config_array:")
print (SENSOR_config_array["Sensors"])	## 40 !!!  it seems to be updated!!!
print()


ret=A10_sensors_class.make_DataString()	## return "maxVal" should =80
print ("3. ret:")
print (ret)								## NO !!
print()

SENSOR_config_array["Sensors"]["Val"]=90
ret=A10_sensors_class.make_DataString2()	## return "maxVal" should =80
print ("4. ret:")
print (ret)								## NO !!
print()

test_class.py

Code: Alles auswählen

#!/usr/bin/env python
# coding: utf8
from collections import defaultdict

class deal_with_sensorData:
	def __init__(self, config_data):
		self.SensorData=defaultdict(set)
		self.SensorData=config_data["Sensors"]
		## config_data has e.g.
		## config_data["Busses"]["type"]
		## config_data["Sensors"]["actualVal"]
		## config_data["Sensors"]["maxVal"]

	def make_DataString(self):
		temp=defaultdict(dict)
		temp["maxVal"]=self.SensorData["maxVal"]*2
		return temp

	def make_DataString2(self):
		temp=defaultdict(dict)
		temp["Val"]=self.SensorData["Val"]*2
		return temp
Für mich ist es etwas schwer zu beschreiben, hoffe aber doch, das das Beispiel helfen kann.

Wie kann ich das Problem der Dict-Erweiterung lösen?

Danke
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

Das löst man, indem man keinen geteilten Zustand verwendet. Warum muß sich denn ein Wert der irgendwo in einer tief verschachtelten Methode einer Instanz gesetzt wird, irgendwo anders in einem ganz anderen Teil einer anderen Methode zu einem anderen Verhalten führen? Diese Art der magischen Fernwirkung will man als Programmierer nicht haben, und versucht das unter allen umständen zu vermeiden.

Was ist Dein eigentliches Problem, bei dem Du fälschlicherweise glaubst, dass Du geteilten Zustand haben möchtest?
Holzbein
User
Beiträge: 9
Registriert: Freitag 16. Dezember 2022, 13:53

Danke für die Antwort.

Die Idee ist, eine Grundstruktur von Sensoren Werten, Zuständen, Infos zu haben.
Und das Programm ist darum herum aufgebaut.

Jetzt gibt es eine Erweiterung.

Anstatt alles zu durchforsten, habe ich eine Funktion, die die Struktur um die neuen Werte erweitert.
Und andere Funktionen können dann auch mit diesen Werten arbeiten. (möglicher Weise alles in der gleichen Klasse)
Ohne das ich sonst umbauen muss.

z.B. Kommt eine Chartfunktion zum Projekt hinzu. Die Grundstruktur hat keine dict-felder für Min/Max, letzte Zeit, Mittelwert......
Ich habe eine Funktion, die ergänzt die Orginalstruktur, andere neue Funktionen berechnen dann Mittelwerte...., schauen auf/generieren die Zeitimpulse für neue Chartpunkte,.....
andere Funtkionen senden das dann an einen Webserver .... oder so.
Alles, ohne die alten Sachen angreifen zu müssen, mit 1-2 Aufrufe im Mainprogramm und gut ist.

So wäre halt meine Idee gewesen. Sollte ich da am Holzweg sein, freu ich mich auf die Diskussion und darauf, was zu lernen.
Benutzeravatar
sparrow
User
Beiträge: 4195
Registriert: Freitag 17. April 2009, 10:28

Grundsätzlich gilt immer: Globale Zustände möchte man nicht.

Alles was auf Modulebene steht (also ohne Einrückung) darf in seinem Wert hinterher nicht mehr verändert werden.
Das gilt auch für deine Datenstruktur.
Funktionen (oder Methoden) erhalten alles, was sie benötigen, als Parameter und geben ihr Ergebnis per return zurück.

Das tust du aber ausufernd und das ist schon die Basis deine Problems.

Um eine Aussage zu treffen, was in deinem Fall besser wäre, lässt sich ohne Details für mich nicht beantworten.


Zum Quellcode: Namen schreibt man in Python klein_mit_unterstrich. Ausgenommen: Konstanten (KOMPLETT_GROSS) und Klassen (PascalCase).
Hinter Funktionsnamen und der öffnenden Klammer gehört kein Leerzeichen.
Eingerückt wird mit 4 Leerzeichen.

Ich sehe hier den Sinn der Klasse nicht.
Da die Klasse keinen Zustand hat (also self nicht sinnvoll verwendet wird), sind die Methoden darin eigentlich Funktionen. Die Klasse ist in meinen Augen unnötig.
Benutzeravatar
__blackjack__
User
Beiträge: 13117
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Das `copy`-Modul ist auch so ein Warnzeichen das man etwas komisches macht, denn das braucht man nur sehr selten. Und auch diese vielen `defaultdict` sehen so aus als wenn da eigentlich in einer anderen Programmiersprache gedacht wird und nicht in Python. Ein tief verschachtelte Grunddatenstruktur ist ebenfalls komisch. Falls das in der Regel feste Sätze an Schlüsseln sind, dann würde ich sogar sagen das ist falsch, denn Abbildungen mit einem festen Satz an Schlüsseln sind eigentlich Objekte.

Die Namen sind inhaltlich komisch. Ist `class` wirklich eine Klasse in der Problemdomäne? Denn in Python ist es dieses Objekt ja nicht. Klassennamen beschreiben „Dinge“ im weitesten Sinne, denn das wird damit modelliert. `deal_with_sensorData` beschreibt aber eine Tätigkeit, das wäre also ein Funktions- oder Methodenname. `make_*` ist auch ein „code smell“, denn *machen* tut ja jede Funktion oder Methode irgend etwas, also ist das im Namen eher eine Nullaussage. Da möchte man wissen *was* da gemacht wird.

In der `__init__()` ist die erste Zeile sinnfrei weil das Objekt das dort erzeugt wird, ja gleich in der nächsten Zeile durch ein anderes ersetzt wird. Ich sehe auch nicht warum diese Klasse den Aufbau von dem übergeordneten Wörterbuch kennen muss und warum man dort nicht gleich die Sensorendaten übergibt.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Holzbein
User
Beiträge: 9
Registriert: Freitag 16. Dezember 2022, 13:53

Danke ihr Beiden
Sicher denke ich in anderen Programmiersprachen und laufe erst sehr langsam mit Python warm.
Auch ist meine Herangehensweise oft sicher (leider) ein Anpassen des Programms an meine Denkweise.
sparrow hat geschrieben: Freitag 16. Dezember 2022, 15:27 Grundsätzlich gilt immer: Globale Zustände möchte man nicht.

Alles was auf Modulebene steht (also ohne Einrückung) darf in seinem Wert hinterher nicht mehr verändert werden.
Das gilt auch für deine Datenstruktur.
Verstehe.
Sagen wir, wir haben eine Grundstruktur bezüglich Sensoren.
Und eine Klasse, die für die SQL Anbindung der vorhandenen Sensorendaten am (lokalen) DBserver zuständig ist.
Jetzt möchte ich mit einer weiteren Klasse spezielle Sensoren (oder Berechnungen) zu meiner Grundstruktur hinzufügen.
Diese Ergänzung soll dann die SQL-Klasse natürlich sehen und verwenden können.
Ich laufe da z.b. alle Sensoren, die es in der Stuktur gibt durch, ergänze DB-Tabellen, wenn ein (neuer) Sensorwert noch nicht existiert, bastle einen SQL String und send den dann an DB oder Webserver....
Damit muss die SQL-Methode dann nicht wissen, wie viele Daten, welche Daten usw. Einfach einen Zweig der Struktur durchgehen.
sparrow hat geschrieben: Freitag 16. Dezember 2022, 15:27 Funktionen (oder Methoden) erhalten alles, was sie benötigen, als Parameter und geben ihr Ergebnis per return zurück.
Das bedeutet, ich Übergebe die Grundstruktur an die Funktion/Methode, bearbeite in der Methode, bekomme einen Returnwert (oder Array oder so), pflege das in die Grundstruktur ein.
So wars eigentlich gedacht, ohne Übergabe an jede Methode einzeln, sondern via __init__ (aber das geht ja nicht ;)
Wahrscheinlich muss ich wirklich für jede Methode extrag übergaben machen und Arrays retourgeben und die dann einpflegen.
sparrow hat geschrieben: Freitag 16. Dezember 2022, 15:27 Das tust du aber ausufernd und das ist schon die Basis deine Problems.
Ja, die Struktur ist schon recht intensiv. zig Sensoren, Zeitwerte, Mittelwerte, Faktoren, Übersetzungen (String->int...).
Die Daten kommen via mqtt von verschiedensten Geräten und alle handhaben ihre Werte anders.
Sehe jetzt im Moment nicht, wie ich das flacher lösen könnte, außer ich betreibe im Hintergrung eine DB.
sparrow hat geschrieben: Freitag 16. Dezember 2022, 15:27 Um eine Aussage zu treffen, was in deinem Fall besser wäre, lässt sich ohne Details für mich nicht beantworten.
Das ist klar. Aber Denkanstöße sind schon sehr wichtig, auch wenn sie allgemein gehalten sind.

sparrow hat geschrieben: Freitag 16. Dezember 2022, 15:27 Zum Quellcode: Namen schreibt man in Python klein_mit_unterstrich. Ausgenommen: Konstanten (KOMPLETT_GROSS) und Klassen (PascalCase).
Hinter Funktionsnamen und der öffnenden Klammer gehört kein Leerzeichen.
Eingerückt wird mit 4 Leerzeichen.
Danke, werde einiges Beherzigen. Einrückungen werde ich aber trotzdem mit TAB machen ;)
sparrow hat geschrieben: Freitag 16. Dezember 2022, 15:27 Ich sehe hier den Sinn der Klasse nicht.
Da die Klasse keinen Zustand hat (also self nicht sinnvoll verwendet wird), sind die Methoden darin eigentlich Funktionen. Die Klasse ist in meinen Augen unnötig.
War auch nur ein dummes Beispiel, um zu zeigen, das Wertänderungen schon, aber Strukturänderungen nicht in der Methode ankommen.
Und ja, ich verwende Methoden sicher eher im Sinne von Funktionen. Ist halt so, wenn man viel ohne Objektorientierung programmiert. Nich so einfach das Umdenken.
Aber ich würde diese Methoden, Funktionen gerne in Klassen zusammen haben, was zusammengehört. Nicht alles in einem rießen File.

__blackjack__ hat geschrieben: Freitag 16. Dezember 2022, 15:46 Das `copy`-Modul ist auch so ein Warnzeichen das man etwas komisches macht, denn das braucht man nur sehr selten. Und auch diese vielen `defaultdict` sehen so aus als wenn da eigentlich in einer anderen Programmiersprache gedacht wird und nicht in Python. Ein tief verschachtelte Grunddatenstruktur ist ebenfalls komisch. Falls das in der Regel feste Sätze an Schlüsseln sind, dann würde ich sogar sagen das ist falsch, denn Abbildungen mit einem festen Satz an Schlüsseln sind eigentlich Objekte.
Da stimmt vieles! Python ist nicht meines bis jetzt. Habe schon vieles hinter mir. Von C64, Assembler, C, Basic, VisualB......, php, damals DBase, access.... da kommt natürlich ein persönlicher Mischmasch raus - sorry.
__blackjack__ hat geschrieben: Freitag 16. Dezember 2022, 15:46 Die Namen sind inhaltlich komisch. Ist `class` wirklich eine Klasse in der Problemdomäne? Denn in Python ist es dieses Objekt ja nicht. Klassennamen beschreiben „Dinge“ im weitesten Sinne, denn das wird damit modelliert. `deal_with_sensorData` beschreibt aber eine Tätigkeit, das wäre also ein Funktions- oder Methodenname. `make_*` ist auch ein „code smell“, denn *machen* tut ja jede Funktion oder Methode irgend etwas, also ist das im Namen eher eine Nullaussage. Da möchte man wissen *was* da gemacht wird.
Man kann sicher vieles mit besseren Namen versehen, aber das ist im Moment nicht der Knackpunkt. Und leider sind mir die Spezialbegriffe z.b. "Problemdomäne" nicht geläufig.
Aber, das wirdsichschon bessern.
__blackjack__ hat geschrieben: Freitag 16. Dezember 2022, 15:46 In der `__init__()` ist die erste Zeile sinnfrei weil das Objekt das dort erzeugt wird, ja gleich in der nächsten Zeile durch ein anderes ersetzt wird.
Wenn du die Kombination

Code: Alles auswählen

   self.SensorData=defaultdict(set)
   self.SensorData=config_data["Sensors"]
meinst, dann braucht es aber dieses defaultdict(set). Mir wäre auch lieber, wenn ich einen Teil der Struktur einfacher hereinholen kann, um damit zu arbeiten.
__blackjack__ hat geschrieben: Freitag 16. Dezember 2022, 15:46 Ich sehe auch nicht warum diese Klasse den Aufbau von dem übergeordneten Wörterbuch kennen muss und warum man dort nicht gleich die Sensorendaten übergibt.
Das wäre durchaus möglich. Mir wurde es so einmal gezeigt, das das Meißte über __init__ läuft, deshalb mach ich das so. Aber ich werden wahrscheinlich umbauen und die benötigten Teilstrukturen immer übergeben.
Das wäre sicher eine, vielleicht DIE Lösung des eigentlichen Problems.
Benutzeravatar
sparrow
User
Beiträge: 4195
Registriert: Freitag 17. April 2009, 10:28

Rück nicht mit Tabstops ein.
Besorg dir einen vernünftigen Editor und der rückt für dich mit 4 Leerzeichen ein. Auch wenn du Tab drückst.
Mit Tabstops einzurücken ist so ziemlich das Schlecteste, was man machen kann.

Und aus deinem Text entnehme ich, dass du einen ganz entscheidenden Denkfehler machst: Klassen sind keine Container für Funktionen. Klassen existieren nicht zum Selbstzweck.
Wenn eine Klasse keinen Zustand repräsentiert, dann ist es keine Klasse.

Wenn du Funktionen auf verschiedene Dateien verteilen willst, dann sind dafür Module in Python das Mittel der Wahl.

Ansonsten wird für mich leider dein Problem nicht klarer. Ich glaube, du klebst viel zu sehr daran, dass es irgend eine Grundstruktur gibt und die ein dict ist.
Holzbein
User
Beiträge: 9
Registriert: Freitag 16. Dezember 2022, 13:53

Danke für die schnelle Antwort
sparrow hat geschrieben: Freitag 16. Dezember 2022, 17:00 Rück nicht mit Tabstops ein.
Besorg dir einen vernünftigen Editor und der rückt für dich mit 4 Leerzeichen ein. Auch wenn du Tab drückst.
Mit Tabstops einzurücken ist so ziemlich das Schlecteste, was man machen kann.
Kann mein Editor natürlich. Aber ich fahre damit ganz gut - egal, ist eher eine Glaubensfrage. Ich werde verrückt, wenn immer diese viele Spaces im Code sind.
sparrow hat geschrieben: Freitag 16. Dezember 2022, 17:00 Und aus deinem Text entnehme ich, dass du einen ganz entscheidenden Denkfehler machst: Klassen sind keine Container für Funktionen. Klassen existieren nicht zum Selbstzweck.
Wenn eine Klasse keinen Zustand repräsentiert, dann ist es keine Klasse.

Wenn du Funktionen auf verschiedene Dateien verteilen willst, dann sind dafür Module in Python das Mittel der Wahl.
Habe ich gerade nachgelesen. Ja, Module sind eigentlich genau das was ich brauche. (Sowas wie include in php)
sparrow hat geschrieben: Freitag 16. Dezember 2022, 17:00 Ansonsten wird für mich leider dein Problem nicht klarer. Ich glaube, du klebst viel zu sehr daran, dass es irgend eine Grundstruktur gibt und die ein dict ist.
Ja, ich denke in Datenstrukturen und Funktionen, um diese zu bearbeiten.Disct deshalb, weil ein Array oder Liste (glaube ich heißts in Python?) keine String-Schlüssel zulassen.
Aus meiner Erfahrung herraus weiß ich leider nicht, wie ich das anders anpacken soll. Aber das ist hier sicher (leider) auch zu viel zu diskutieren.
Ich denke, die Hinweise auf
* Datenübergabe bei jedem Funktionsaufruf und kein __init__
* Funktionen und keine Methoden
* Module und keine Klasse
sind im Moment DIE Lösung für mich.

Wenn es weiter Anmerkungen gibt, bitte sehr gerne.
Ansonsten viele Dank,
Holzbein
Benutzeravatar
__blackjack__
User
Beiträge: 13117
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Holzbein: SQL als Zeichenketten basteln klingt dann schon nach der nächste „Alarmglocke“. Das macht man eigentlich auch nicht, für so etwas gibt es beispielsweise SQLAlchemy. Da würde ich an Deiner Stelle mal schauen ob Du das nicht gerade selbst irgendwie nachprogrammierst, was es dort schon fertig gibt.

Was meinst Du mit Arrays? Du hast da keine gezeigt, es gibt in Python aber tatsächlich Arrays — damit sind in der Regel Numpy-Arrays gemeint.

Warum Einrückung mit TAB? Das macht nur Probleme. Es ist nicht klar wie weit ein Tab eingerückt wird und wenn man irgendwo Tab und Leerzeichen vermischt, funktioniert Code unter umständen anders als es optisch den Anschein macht. Das ist auch keine Glaubensfrage. Das ist entschieden. Ich werde immer ganz verrückt wenn das jemand anders macht. Wie jeder andere Python-Programmierer auch. 😉

Die Organisationseinheit für Funktionen ist das Modul, nicht die Klasse.

„Problemdomäne“ ist kein Python-spezifischer Begriff. Das ist die Begriffswelt von dem Problem das gelöst werden soll, da kann der Begriff „Klasse“ ja auch eine Bedeutung haben. Also beispielsweise wenn man Fahrzeuge modelliert, beispielsweise Schiffe, dann kann es dort Schiffsklassen geben, die nichts mit der Klasse in der Programmierung zu tun hat. So könnte es auch eine Sensorklasse geben in der realen Welt. Dann kann man `class` in einem Namen dafür im Programm verwenden. Wenn der Wert dort nichts mit einer Sensorklasse in der realen Welt zu tun hat, dann ist er falsch, denn programmtechnisch ist das ja keine Klasse was dort an den Namen gebunden wird.

Um Klassen zu verwenden muss man nicht zwingend objektorientiert denken, denn das ist in Python halt auch der Verbunddatentyp. Also da wo man in anderen Programmiersprachen ``struct``, TYPE, RECORD, oder Ähnliches verwendet um einen Satz an Daten zu beschreiben und zusammenzufassen und als *einen* Wert ansprechen zu können, verwendet man in Python auch schon eine Klasse. Das sind in den älteren Sprachen in der Regel auch die Sprachkonstrukte, die zu Objekten/Klassen weiterentwickelt wurden. Python hat diesen Zwischenschritt einfach ausgelassen, weil es nicht gross Sinn macht neben der Klasse, die das ja mit abdecken kann, extra noch etwas anderes einzuführen. (Oder man könnte das zumindest denken, denn Java 14 führt ``record``-Klassen ein.)

Die Kombination meinte ich und die erste Zeile braucht es dort nicht, die hat keinerlei Auswirkung als einfach ein bisschen Rechenzeit und kurz Speicher zu verbrauchen, denn das `defaultdict`-Objekt wird erzeugt und gleich in der nächsten Zeile ja wieder verworfen ohne das irgend etwas damit gemacht worden wäre.

Und mir ging es ja gerade nicht um das herein*holen* sondern das die `__init__()` bereits die richtigen Daten übergeben bekommt, statt sich die aus einer übergeordneten Struktur selbst heraus zu holen. Für Tests muss man hier ja eine Datenstruktur erstellen die nur aus einem Schlüssel-/Wert-Paar besteht und die nur existiert damit sich die Methode diesen einen Wert da heraus holen kann.

Bei solchen Wrapperklassen um Einzelwerte ist immer die Frage ob und welchen Mehrwert die bieten. Das kann Vorteile bieten wenn es den Code lesbarer macht und/oder Polymorphie möglich macht, aber wenn es am Ende einfach nur die Schreibweise ``data.do_something()`` vs. ``do_something(data)`` ist, finde ich das nicht genug um eine Klasse mit einem Attribut zu rechtfertigen.

Und „das meisste“ sollte nicht in `__init__()` passieren. Das sollte in aller Regel eher so wenig wie möglich passieren. Meistens nur Zuweisung der Attribute. Validierung und einfache Konvertierungen kann da noch dazu kommen. Alles was komplexer ist, sollte dort nicht stehen, sonst leidet schnell die Testbarkeit der Objekte, weil man dann nicht mehr so einfach Objekte in einem bestimmten Zustand erstellen kann. Wenn komplexere Schritte nötig sind um die Werte für den Zustand vorzubereiten, würde ich das in Klassenmethoden unterbringen.

``include`` in PHP macht etwas anderes. Das fügt ja quasi an der Stelle den Inhalt der anderen Datei ein. In Python wird jedes Modul genau einmal geladen und ausgeführt, egal wie oft ``import`` mit diesem Modulnamen irgendwo ausgeführt wird. Beim ersten ``import`` passiert das, und bei jedem weiteren ``import`` wird einfach nur das bereits existierende Modul verwendet. Und das hat einen eigenen Namensraum.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Benutzeravatar
grubenfox
User
Beiträge: 432
Registriert: Freitag 2. Dezember 2022, 15:49

Holzbein hat geschrieben: Freitag 16. Dezember 2022, 17:17 Ja, ich denke in Datenstrukturen und Funktionen, um diese zu bearbeiten.
Das kenne ich (5 Tage die Woche weil die genutzte Prog.sprache nicht mehr her gibt). Privat nehme ich dann lieber Python weil man da alle Möglichkeiten hat (z.b. Funktionen, aber eben auch Objektorientiert mit Klassen usw.).
__blackjack__ hat geschrieben: Freitag 16. Dezember 2022, 17:40 „Problemdomäne“ ist kein Python-spezifischer Begriff.
Ich hätte da auch noch einen Begriff: Entwurfsmuster...
Aber ich kann gerade nicht greifen welche Entwurfsmuster da sinnvoll sein könnten. (und bin auch nicht sicher ob der Hinweis hier jetzt so zielführend ist oder nur die Verwirrung vergrößert.)
Man müsste mal versuchen das ganze Problem mal sauber in Klassen auf zu teilen bzw. zu zerlegen. Aber aufgrund der zeitlichen Umstände.... [Wochenende und fortgeschrittene Stunde ;)]

Also nur ein kurzer allererster Versuch...

Code: Alles auswählen

class Messwert(object):
	pass
	
class Sensor(object):
	pass
	
class Auswertung(object):
	pass

class SensorAnsteuerung(object):
	pass
Bin jetzt nicht sicher ob 'Messwert' und 'SensorAnsteuerung' als Klasse sich hier als sinnvoll erweisen, aber jedenfalls haben wir Sensoren und irgendwelche Auswertungen... welche Daten und welches Verhalten dann in diese Klassen müsste, wäre dann zu klären.
Holzbein
User
Beiträge: 9
Registriert: Freitag 16. Dezember 2022, 13:53

__blackjack__ hat geschrieben: Freitag 16. Dezember 2022, 17:40 @Holzbein: SQL als Zeichenketten basteln klingt dann schon nach der nächste „Alarmglocke“. Das macht man eigentlich auch nicht, für so etwas gibt es beispielsweise SQLAlchemy. Da würde ich an Deiner Stelle mal schauen ob Du das nicht gerade selbst irgendwie nachprogrammierst, was es dort schon fertig gibt.
Danke für den Hinweis. Es gibt sicher 100erte Klassen, die mir das Leben erleichtern würden. Nur kennen sollte man die. - Ich schau mir das gerne an.
Edit: habe mir jetzt Alchemy angeschaut. Auf den ersten Blick nur ein Wrapper für SQL. Da ist für mich jetzt kein großer Unterschied ob ich SQL Strings zusammenbau oder mit 'add_all' hinzufüge und die Objekte vorher zusammenstelle. Die Unabhängikeit von der DBengine im Hintergrund ist natürlich verlockend. Werde das weiter verfolgen.
__blackjack__ hat geschrieben: Freitag 16. Dezember 2022, 17:40 Was meinst Du mit Arrays? Du hast da keine gezeigt, es gibt in Python aber tatsächlich Arrays — damit sind in der Regel Numpy-Arrays gemeint.
Sorry - für mich sind Array alles, was in einem Datenbaum strukturierte Daten speichert. Ob die Dict wie in Python oder anders heißen.....
__blackjack__ hat geschrieben: Freitag 16. Dezember 2022, 17:40 Die Organisationseinheit für Funktionen ist das Modul, nicht die Klasse.
Das war schon ein sehr guter Hinweis. Kommt mir und meiner Arbeitsweise näher. Habe ich , vor dieser Diskussion hier, nicht gekannt.
__blackjack__ hat geschrieben: Freitag 16. Dezember 2022, 17:40 „Problemdomäne“ ist kein Python-spezifischer Begriff. Das ist die Begriffswelt von dem Problem das gelöst werden soll, da kann der Begriff „Klasse“ ja auch eine Bedeutung haben. Also beispielsweise wenn man Fahrzeuge modelliert, beispielsweise Schiffe, dann kann es dort Schiffsklassen geben, die nichts mit der Klasse in der Programmierung zu tun hat. So könnte es auch eine Sensorklasse geben in der realen Welt. Dann kann man `class` in einem Namen dafür im Programm verwenden. Wenn der Wert dort nichts mit einer Sensorklasse in der realen Welt zu tun hat, dann ist er falsch, denn programmtechnisch ist das ja keine Klasse was dort an den Namen gebunden wird.
OK
__blackjack__ hat geschrieben: Freitag 16. Dezember 2022, 17:40 Um Klassen zu verwenden muss man nicht zwingend objektorientiert denken, denn das ist in Python halt auch der Verbunddatentyp. Also da wo man in anderen Programmiersprachen ``struct``, TYPE, RECORD, oder Ähnliches verwendet um einen Satz an Daten zu beschreiben und zusammenzufassen und als *einen* Wert ansprechen zu können, verwendet man in Python auch schon eine Klasse. Das sind in den älteren Sprachen in der Regel auch die Sprachkonstrukte, die zu Objekten/Klassen weiterentwickelt wurden. Python hat diesen Zwischenschritt einfach ausgelassen, weil es nicht gross Sinn macht neben der Klasse, die das ja mit abdecken kann, extra noch etwas anderes einzuführen. (Oder man könnte das zumindest denken, denn Java 14 führt ``record``-Klassen ein.)
Das wird mein Problem mit Python sein. Und da muss ich erst umdenken lernen.
__blackjack__ hat geschrieben: Freitag 16. Dezember 2022, 17:40 Die Kombination meinte ich und die erste Zeile braucht es dort nicht, die hat keinerlei Auswirkung als einfach ein bisschen Rechenzeit und kurz Speicher zu verbrauchen, denn das `defaultdict`-Objekt wird erzeugt und gleich in der nächsten Zeile ja wieder verworfen ohne das irgend etwas damit gemacht worden wäre.
Also, bei mir läuft das ohne dieser Zeile nicht
__blackjack__ hat geschrieben: Freitag 16. Dezember 2022, 17:40 Und mir ging es ja gerade nicht um das herein*holen* sondern das die `__init__()` bereits die richtigen Daten übergeben bekommt, statt sich die aus einer übergeordneten Struktur selbst heraus zu holen. Für Tests muss man hier ja eine Datenstruktur erstellen die nur aus einem Schlüssel-/Wert-Paar besteht und die nur existiert damit sich die Methode diesen einen Wert da heraus holen kann.
Und genau das war das Ursprungsproblem - Ursprungsfrage.
Ich übergebe in __init__ Daten. Wenn 'außen' dann Werte geändert werden, erscheinen die jedoch nur teilweise in der Klasse geändert.
Ich habe auch nur Teil-Daten-Strukturen übergeben. Eben den Teil, den die Klasse und ihre Methoden (oder was ich dafür gehalten habe) braucht.
__blackjack__ hat geschrieben: Freitag 16. Dezember 2022, 17:40 Bei solchen Wrapperklassen um Einzelwerte ist immer die Frage ob und welchen Mehrwert die bieten. Das kann Vorteile bieten wenn es den Code lesbarer macht und/oder Polymorphie möglich macht, aber wenn es am Ende einfach nur die Schreibweise ``data.do_something()`` vs. ``do_something(data)`` ist, finde ich das nicht genug um eine Klasse mit einem Attribut zu rechtfertigen.
Mit dem Hinweis auf Module sehe ich das genauso.
__blackjack__ hat geschrieben: Freitag 16. Dezember 2022, 17:40 Und „das meisste“ sollte nicht in `__init__()` passieren. Das sollte in aller Regel eher so wenig wie möglich passieren. Meistens nur Zuweisung der Attribute. Validierung und einfache Konvertierungen kann da noch dazu kommen. Alles was komplexer ist, sollte dort nicht stehen, sonst leidet schnell die Testbarkeit der Objekte, weil man dann nicht mehr so einfach Objekte in einem bestimmten Zustand erstellen kann. Wenn komplexere Schritte nötig sind um die Werte für den Zustand vorzubereiten, würde ich das in Klassenmethoden unterbringen.
Bei meinem (großen) Programm, ist das so. In __init__ nur Datenübergabe, Zuordnung von Teilstrukturen in Variablen mit klingenden Namen. Bearbeitung natürlich in den Methoden.
__blackjack__ hat geschrieben: Freitag 16. Dezember 2022, 17:40 ``include`` in PHP macht etwas anderes. Das fügt ja quasi an der Stelle den Inhalt der anderen Datei ein. In Python wird jedes Modul genau einmal geladen und ausgeführt, egal wie oft ``import`` mit diesem Modulnamen irgendwo ausgeführt wird. Beim ersten ``import`` passiert das, und bei jedem weiteren ``import`` wird einfach nur das bereits existierende Modul verwendet. Und das hat einen eigenen Namensraum.
Hast recht, nicht ganz das Gleiche.


grubenfox hat geschrieben: Samstag 17. Dezember 2022, 00:04 Bin jetzt nicht sicher ob 'Messwert' und 'SensorAnsteuerung' als Klasse sich hier als sinnvoll erweisen, aber jedenfalls haben wir Sensoren und irgendwelche Auswertungen... welche Daten und welches Verhalten dann in diese Klassen müsste, wäre dann zu klären.
Danke. für den Versuch. Wollte aber nicht, das jemand sich mit dem großen Programm herumplagt.
Aber vielleicht kurz zur Erklärung.


Ich bekomme via MQTT Sensorendaten. Als Floatwerte, Integerwerte, Stringwerte. Je nach angeschlossenem Sensor.
Manche Werte müssen auch via Modbus gepollt werden.
Stringwerte müssen erst über eine Translatetabelle umgesetzt werden, damit ALLES als float. Macht so im ersten Moment wenig Sinn, ist aber für das einfache DBhandling dann gut.
Kommt ein neuer Sensorwert hinzu, werden auch von passenden Methoden/Funktionen die DB-Tabellen erweitert. Ob und wie steht wieder, zum Sensorwert passend, in dieser 'ominösen' Datenstruktur.

Diese Werte werden gesammelt. Es gibt 4 Zeiträume für die Charts. 2h, 12h, 2Tage, 7Tage.
Bei manchen Werten benötige ich Min- Maximalwerte innerhalb des Zeitraumes, bei anderen ist es einfach der (zeitlich zufällige) Momentanwert, manchmal Mittelwerte.
Bei manchen Werten müssen Faktoren den Wert erst bewerten.

Dann kommen Funktionen dazu, die z.b. in einem Zeitraum Oktober - März und passender Urzeit basierend auf verschiedenen Sensorwerten (Temperaturen) Heizkreise ein- und ausschaltet. Oder, wenn Grenztemperaturen erreicht werden, werden andere Pumpen, Ventile geschaltet .....
Gesendet dann via MQTT oder Modbus oder teilweise anderen Protokollen. Oder auch direkt via GPIOs am RaspPi.

Das alles via Nodered bedienbar. Und mit Charts (JavaScript), die aus einer DB die zeitlich und Wertmäßig aufbereiteten fertige Daten nehmen.
Diese DB muss passend gefüllt werden.
Also auch ein SQL Teil, welcher die richtigen Werte zusammen sucht und in DB einlagert. Welche Werte das sind ist wieder für jeden Wert, eben in dieser, meinen "ominösen" Datenstruktur definiert.
Und es wird ein SQL String generiert, welcher als String auf einen Webserver überspielt wird, damit dort das PHP Programm die Werte wiederum in ein DB einlagert, um auch außerhalb des Heimnetzes Werte und Bedienungen zu haben.

Ach ja, natürlich müssen die Bedienungen von der Webseite auch gepollt werden, um im Heimnetz zu wirken.

Ich mach deshalb diese "ominösen" Datenstruktur. Dort stehen meine Sensor Daten drinnen, passend auch, wie das Rohdatum behandelt werden soll, die Max- Min- Mittelwerte zum Datum passend.

Kommt nun ein Sensor dazu, ergänze ich die Datenstruktur um diesen Sensor. Da ist dann mit 3-5 Zeilen ALLES definiert, was mit dem Wert passieren muss, damit die einzelnen 'Funktionen' damit arbeiten können / dürfen.
Und kommt eine neu Funktionalität zum Programm hinzu : Neue/weiter Klasse/Methode, einbindung im Mainprogramm, eventuell Parameterergänzung in Datenstruktur und fertig.

Für mich hatte das nach Klassen geklungen, die 'nur' ihren Teil machen und ein Ergebnis 'ausspucken', das dann wiederum eventuell in die 'Datenstruktur' eingelagert wird.
Damit hätte ich den Vorteil, das die Bearbeitungsfunktion nur in der Klasse (oder Funktion) zu betrachten ist, Sensoreigenschaften nur in der Datenstruktur usw.

Und eigentlich funktioniert mein Konzept auch sehr gut. Änderungen sind extrem einfach zu machen.
Bis ich über das Orginalproblem stolperte, dass in gewissen Fällen in diesem Dict geänderte Daten dann in den Klassen/Methoden nicht geändert erscheinen.
Aber das lässt sich mit Funktionen in Modulen mit Parameterübergabe lösen.
(Das einfachste wäre Übergabe als reference, dann könnte die Datenstruktur direkt geändert werden, aber das spielts in Python nicht. :mrgreen: )

Aber ich wollte euch da eigentlich nicht mit dem großen Projekt zuquatschen.
Danke für euer Mitdenken.



D
Benutzeravatar
sparrow
User
Beiträge: 4195
Registriert: Freitag 17. April 2009, 10:28

Zu SQLAlchemy:
Der Wrapper ist nur eine Schicht. Es ist vor allem ein ORM. Also etwas, das Relationen in einer Datenbank direkt als Objekte abbildet.
Mein Bauchgefühl sagt mir auch, dass du die Anfragen an SQL falsch "zusammenbaust".

Zu Arrays:
Wenn alle Datenstrukturen für dich ein Array sind, dann musst du dir das abgewöhnen.
Arrays sind spezielle Datenstrukturen die in Numpy verwendet werden. Python kennt als veränderliche Datenstrukturen Listen, Sets und Dicts.
Wenn für dich ein Dict ein Array ist, dann bist du in irgend einer deiner vorher aufgezählten Sprachen falsch abgebogen.
Benenne Datenstrukturen richtig, sonst kann niemand entnehmen, was du meinst.
"Der Elefant war ganz schön schnell". - "Das war ein Auto." - "Ja neee, sorry. Für mich ist alles mit vier Reifen ein Elefant."

Der große Prosa-Text hilft nur bedingt weiter, denn da ist schon zu viel "ich habe das so umgestzt" drin. Klingt nach XY Problem.
Jetzt klingt es für mich sogar ein bisschen so als würde die ominöse Datenstruktur parralel zur Datenbank existieren.
Holzbein
User
Beiträge: 9
Registriert: Freitag 16. Dezember 2022, 13:53

sparrow hat geschrieben: Samstag 17. Dezember 2022, 08:50 Zu SQLAlchemy:
Der Wrapper ist nur eine Schicht. Es ist vor allem ein ORM. Also etwas, das Relationen in einer Datenbank direkt als Objekte abbildet.
Eben. Der Wrapper hat den Vorteil, das das dann auf verschiedene DB Engines angewandt werden kann, ohne Quelltext zu ändern.
sparrow hat geschrieben: Samstag 17. Dezember 2022, 08:50 Mein Bauchgefühl sagt mir auch, dass du die Anfragen an SQL falsch "zusammenbaust".
Ich denke, das passt schon.
sparrow hat geschrieben: Samstag 17. Dezember 2022, 08:50 Zu Arrays:
Wenn alle Datenstrukturen für dich ein Array sind, dann musst du dir das abgewöhnen.
Arrays sind spezielle Datenstrukturen die in Numpy verwendet werden. Python kennt als veränderliche Datenstrukturen Listen, Sets und Dicts.
Wenn für dich ein Dict ein Array ist, dann bist du in irgend einer deiner vorher aufgezählten Sprachen falsch abgebogen.
Benenne Datenstrukturen richtig, sonst kann niemand entnehmen, was du meinst.
"Der Elefant war ganz schön schnell". - "Das war ein Auto." - "Ja neee, sorry. Für mich ist alles mit vier Reifen ein Elefant."
Das stimme ich dir unbedingt zu.
sparrow hat geschrieben: Samstag 17. Dezember 2022, 08:50 Der große Prosa-Text hilft nur bedingt weiter, denn da ist schon zu viel "ich habe das so umgestzt" drin. Klingt nach XY Problem.
Jetzt klingt es für mich sogar ein bisschen so als würde die ominöse Datenstruktur parralel zur Datenbank existieren.
War auch nur so zum Drüberlesen gedacht.

Ich denke, im Moment habe ich genügend Input zu verarbeiten. Das schwirrt schon mal der Kopf.
Und mal schauen, ob der Andere Lösungsweg (Module) im Moment zum Ziel führt.

Danke für die Zeit und die Ideen!
Schönen Sonntag noch.
Benutzeravatar
bwbg
User
Beiträge: 407
Registriert: Mittwoch 23. Januar 2008, 13:35

Holzbein hat geschrieben:Und es wird ein SQL String generiert, welcher als String auf einen Webserver überspielt wird, damit dort das PHP Programm die Werte wiederum in ein DB einlagert, um auch außerhalb des Heimnetzes Werte und Bedienungen zu haben.
Hier klingeln gerade ziemlich laute Alarmglocken :!:
"Du bist der Messias! Und ich muss es wissen, denn ich bin schon einigen gefolgt!"
Holzbein
User
Beiträge: 9
Registriert: Freitag 16. Dezember 2022, 13:53

bwbg hat geschrieben: Samstag 17. Dezember 2022, 10:14
Holzbein hat geschrieben:Und es wird ein SQL String generiert, welcher als String auf einen Webserver überspielt wird, damit dort das PHP Programm die Werte wiederum in ein DB einlagert, um auch außerhalb des Heimnetzes Werte und Bedienungen zu haben.
Hier klingeln gerade ziemlich laute Alarmglocken :!:
Warum?
Benutzeravatar
bwbg
User
Beiträge: 407
Registriert: Mittwoch 23. Januar 2008, 13:35

Die Beschreibung lässt den starken Verdacht zu, dass es auf deinem (öffentlichen) Webserver eine Schnittstelle gibt, die es erlaubt, beliebige SQL-Queries auf die Datenbank abzusetzen.
"Du bist der Messias! Und ich muss es wissen, denn ich bin schon einigen gefolgt!"
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Der übliche Link: https://xkcd.com/327/
Benutzeravatar
grubenfox
User
Beiträge: 432
Registriert: Freitag 2. Dezember 2022, 15:49

Holzbein hat geschrieben: Samstag 17. Dezember 2022, 08:10 (Das einfachste wäre Übergabe als reference, dann könnte die Datenstruktur direkt geändert werden, aber das spielts in Python nicht. :mrgreen: )
Also wenn es nicht eh schon klar wäre dass wir ein Problem haben.. dann jetzt: in Python ist die Übergabe immer per Reference und ich habe mal gelernt ein häufig gemachter Anfängerfehler sei, in Funktionen/Methoden übergebene veränderbare Datenstrukturen zu ändern um sich dann außerhalb der Funktionen/Methode zu wundern warum die Datenstrukturen auf einmal kaputt ist.

Um das zu vermeiden mittels copy.deepcopy() eine Kopie der übergebenen Daten machen... falls nötig.. eventuell reicht auch copy.copy(). [Hier jetzt vielleicht nicht gewünscht.]

P.S.:
Holzbein hat geschrieben: Samstag 17. Dezember 2022, 09:57 Danke für die Zeit und die Ideen!
Schönen Sonntag noch.
Zum Glück haben wir noch Samstag! :D
Benutzeravatar
grubenfox
User
Beiträge: 432
Registriert: Freitag 2. Dezember 2022, 15:49

sparrow hat geschrieben: Samstag 17. Dezember 2022, 08:50 Zu Arrays:
Wenn alle Datenstrukturen für dich ein Array sind, dann musst du dir das abgewöhnen.
Arrays sind spezielle Datenstrukturen die in Numpy verwendet werden. Python kennt als veränderliche Datenstrukturen Listen, Sets und Dicts.
Also es gibt schon immer(? mindestens seit Python 2.2.2) das Modul array in der Standard Lib. von Python. Kann im Gegensatz zu Arrays in anderen Sprachen aber nur Zahlen speichern und ist nicht mit den Numpy-Arrays zu verwechseln.

(Im deutschen "Python kurz & gut" von 1999 (Python 1.5.1) steht hinten "array - Schnittstelle zu C-Vektoren in Python". 'Vektoren' halte ich hier für einen Übersetzungsfehler. Waren wohl Zahlen mit gemeint. [ab und zu ganz nett dass ich keine Bücher wegwerfen kann :) ])
Benutzeravatar
__blackjack__
User
Beiträge: 13117
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@grubenfox: Das Modul ist aber irrelevant sowohl bei der Diskussion hier über Datentypen als auch in der Praxis. Da meint man mit Arrays in Python eigentlich immer Numpy-Arrays. Falls etwas anderes gemeint ist, wird das in der Regel immer dazu gesagt, und selbst dann sind das keine `array`-Arrays, sondern in der Regel `ctypes.Array`-Objekte, denn auch das wird deutlich öfter verwendet als das `array`-Modul.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Antworten