Seite 1 von 1
Dataclass: Attribute automatisch berechnen
Verfasst: Dienstag 24. Juni 2025, 10:54
von noisefloor
Hallo,
folgende Problemstellung: es soll eine Durchflussrate umgerechnet werden, es gibt fünf mögliche Einheiten. Wenn man den Wert für eine Einheit vorgibt, sollen die anderen vier automatisch berechnet werden.
Mein Ansatz bisher ist über eine DataClass:
Code: Alles auswählen
from dataclasses import dataclass
@dataclass
class Flowrate():
_kg_to_lbs: float = 2.20462262
kg_per_min: int = None
kg_per_h: int = None
t_per_h: int = None
lbs_per_min: int = None
lbs_per_h: int = None
def __post_init__(self):
if self.kg_per_min:
self.kg_per_h = self.kg_per_min * 60
self.t_per_h = self.kg_per_h / 1000
self.lbs_per_min = round(self.kg_per_min * self._kg_to_lbs, 0)
self.lbs_per_h = self.lbs_per_min * 60
if self.kg_per_h:
self.kg_per_min = int(self.kg_per_h / 60)
self.t_per_h = self.kg_per_h / 1000
self.lbs_per_min = round(self.kg_per_min * self._kg_to_lbs, 0)
self.lbs_per_h = self.lbs_per_min * 60
#und so weiter
Das funktioniert auch:
Code: Alles auswählen
>>> >>> f = Flowrate(kg_per_min = 100)
>>> f
Flowrate(_kg_to_lbs=2.20462262, kg_per_min=100, kg_per_h=6000, t_per_h=6.0, lbs_per_min=220.0, lbs_per_h=13200.0)
aber logischerweise nur bei der Instanzierung. Wenn ich dann z.B. `f.kg_per_min = 200` aufführen, ändern sich die anderen vier Attribute nicht mehr.
Theoretisch sollte man doch setter für jedes Attribut schreiben können, die dann die andere vier Attribute ändern. Wenn das gehen sollte checke ich nicht, wie das geht.
Es muss auch keine DataClass sein, es ginge auch jedes andere Objekt, was die fünf Attribute hat und die Werte neu berechnet, wenn man ein Attribut ändert.
Gruß, noisefloor
Re: Dataclass: Attribute automatisch berechnen
Verfasst: Dienstag 24. Juni 2025, 11:53
von Dennis89
Hallo,
spontan würde mir das einfallen, ist aber bei mehreren Werten etwas "schreib"-aufwändig:
Code: Alles auswählen
from dataclasses import dataclass
@dataclass
class Flowrate:
_kg_per_min = 2.20462262
kg_per_h = None
def __post_init__(self):
self.calculate_units()
def clear_values(self):
self._kg_per_min = None
self.kg_per_h = None
def calculate_units(self):
if self._kg_per_min:
self.kg_per_h = self._kg_per_min * 60
elif self.kg_per_h:
self.kg_per_min = int(self.kg_per_h / 60)
@property
def kg_per_min(self):
return self._kg_per_min
@kg_per_min.setter
def kg_per_min(self, value):
self.clear_values()
self._kg_per_min = value
self.calculate_units()
def main():
flowrate = Flowrate()
print(flowrate.kg_per_h)
flowrate.kg_per_min = 100
print(flowrate.kg_per_h)
if __name__ == "__main__":
main()
`attrs`hat einen `converter`, aber ich glaube nicht dass der hilft. Macht es Sinn eine neue Instanz zu erstellen, wenn sich ein Wert ändert?
Grüße
Dennis
Re: Dataclass: Attribute automatisch berechnen
Verfasst: Dienstag 24. Juni 2025, 12:04
von __blackjack__
@noisefloor: Das klingt eher nach einem Fall für Properties.
Code: Alles auswählen
class Flowrate:
_KG_TO_LBS = 2.20462262
def __init__(self, kg_per_h=0):
self.kg_per_h = kg_per_h
@property
def kg_per_min(self):
return self.kg_per_h // 60
@kg_per_min.setter
def kg_per_min(self, value):
self.kg_per_h = value * 60
@property
def t_per_h(self):
return self.kg_per_h // 1000
@t_per_h.setter
def t_per_h(self, value):
self.kg_per_h = value * 1000
@property
def lbs_per_min(self):
return int(round(self.kg_per_min * self._KG_TO_LBS))
@lbs_per_min.setter
def lbs_per_min(self, value):
self.kg_per_min = int(round(value / self._KG_TO_LBS))
from_kg_per_h = __init__
@classmethod
def from_t_per_h(cls, value):
result = cls()
result.t_per_h = value
return result
@classmethod
def from_lbs_per_min(cls, value):
result = cls()
result.lbs_per_min = value
return result
Wobei ich das generell in Frage stellen würde diese ganzen Attribute zu haben. Ich würde da eher schauen ob eine Bibliothek wie `pint` mich da weiter bringt.
Code: Alles auswählen
In [102]: ur = pint.UnitRegistry()
In [103]: ur.kilogram / ur.hour
Out[103]: <Unit('kilogram / hour')>
In [104]: kg_per_hour = ur.kilogram / ur.hour
In [105]: lbs_per_minute = ur.pound / ur.minute
In [106]: x = 10 * kg_per_hour
Out[106]: <Quantity(10, 'kilogram / hour')>
In [107]: x.to(lbs_per_minute)
Out[107]: <Quantity(0.367437104, 'pound / minute')>
Re: Dataclass: Attribute automatisch berechnen
Verfasst: Dienstag 24. Juni 2025, 12:21
von noisefloor
Hallo,
Danke für das Feedback. Was glaube ich in meinen Ausgangpost nicht ganz klar rüber kam: das soll in alle fünf Richtungen funktionieren. Also wenn man meine DataClass nehmen würde und mit `f = Flowrate(lbs_per_h=40000)` instanziert, sollen kg_per_min usw. berechnet werden. Es muss nicht immer `kg_per_min` als Argument übergeben werden.
Genutzt werden soll das später im Backend einer Webanwendung: es gibt eine Formular mit einem Eingabewert für einen positiven Interger Wert und ein Auswahlmenü für die fünf Einheiten. Nach dem Übertragen sollen im Backend die andere vier Werte berechnet werden und dann als HTML-Seite ausgegeben.
Gruß, noisefloor
Re: Dataclass: Attribute automatisch berechnen
Verfasst: Dienstag 24. Juni 2025, 12:31
von Dennis89
das soll in alle fünf Richtungen funktionieren
Das wäre so möglich. Ist nur ein Beispiel die Umrechnungen und andere Werte müssen noch ergänzt werden.
Code: Alles auswählen
from attrs import define, field
@define
class Flowrate:
_kg_per_min = field(default=None)
_kg_per_h = field(default=None)
def __attrs_post_init__(self):
self.calculate_units()
def clear_values(self):
self._kg_per_min = None
self._kg_per_h = None
def calculate_units(self):
if self._kg_per_min:
self._kg_per_h = self._kg_per_min * 60
elif self.kg_per_h:
self.kg_per_min = int(self._kg_per_h / 60)
@property
def kg_per_h(self):
return self._kg_per_h
@property
def kg_per_min(self):
return self._kg_per_min
@kg_per_min.setter
def kg_per_min(self, value):
self.clear_values()
self._kg_per_min = value
self.calculate_units()
def main():
flowrate = Flowrate(kg_per_min=200)
print(flowrate.kg_per_h)
flowrate.kg_per_min = 100
print(flowrate.kg_per_h)
if __name__ == "__main__":
main()
Grüße
Dennis
Re: Dataclass: Attribute automatisch berechnen
Verfasst: Dienstag 24. Juni 2025, 13:10
von __blackjack__
@noisefloor: Ich sehe da nicht zwingend eine Klasse und schon mal gar nicht, dass das Objekt dafür veränderbar sein muss, also dass man nicht einfach bei einem neuen Wert ein neues Objekt erstellen kann, statt diesen einen Wert neu zu setzen und die anderen neu zu berechnen.
Wenn man `pint` nicht verwenden möchte, dann könnte man aber zumindest den Ansatz übernehmen so etwas wie zusammengesetzte Werte + Einheiten zu haben und die ”umrechenbar” zu machen.
Code: Alles auswählen
#!/usr/bin/env python3
from collections import namedtuple
from pint import UnitRegistry
UNITS = ["kg per min", "kg per h", "t per h", "lbs per min", "lbs per h"]
Flowrate = namedtuple("Flowrate", [unit.replace(" ", "_") for unit in UNITS])
def calculate_flowrate(units, flow_rate_text):
flow_rate = units[flow_rate_text]
return Flowrate(*(round(flow_rate.to(unit).magnitude) for unit in UNITS))
def main():
units = UnitRegistry()
print(calculate_flowrate(units, "100 kg per min"))
if __name__ == "__main__":
main()
Ausgabe:
Code: Alles auswählen
Flowrate(kg_per_min=100, kg_per_h=6000, t_per_h=6, lbs_per_min=220, lbs_per_h=13228)
Re: Dataclass: Attribute automatisch berechnen
Verfasst: Dienstag 24. Juni 2025, 13:52
von kbr
Ich würde auch den Ansatz mit Properties wählen und den Wert selbst nur über ein Attribut (z. B. kg pro min) speichern. Die Getter und Setter rechnen dann gegen dieses Attribut. Eine dataclass brauchst du nicht.
Re: Dataclass: Attribute automatisch berechnen
Verfasst: Dienstag 24. Juni 2025, 13:56
von DeaD_EyE
Ich würde immer auf die SI-Einheit zurückfallen, properties für die anderen Einheiten verwenden und mit Klassenmethoden von anderen Einheiten instanziieren.
Code: Alles auswählen
from dataclasses import dataclass
@dataclass
class FlowRate:
m3_p_s: float
@property
def l_p_s(self) -> float:
"""Liters per second."""
return self.m3_p_s * 1000
@property
def m3_p_h(self) -> float:
"""Cubic meters per hour."""
return self.m3_p_s * 3600
@classmethod
def from_l_p_s(cls, l_p_s: float) -> "FlowRate":
"""Create FlowRate from liters per second."""
return cls(m3_p_s=l_p_s / 1000)
@classmethod
def from_m3_p_h(cls, m3_p_h: float) -> "FlowRate":
"""Create FlowRate from cubic meters per hour."""
return cls(m3_p_s=m3_p_h / 3600)
fr = FlowRate.from_l_p_s(10.0)
print(fr)
print(fr.m3_p_s)
print(fr.l_p_s)
print(fr.m3_p_h)
Re: Dataclass: Attribute automatisch berechnen
Verfasst: Dienstag 24. Juni 2025, 13:58
von Kebap
Was ist denn effektiver? Die 5 Werte zu speichern, oder nur einen speichern, aber bei jeder Ausgabe die 4 fehlenden neu zu berechnen?
Re: Dataclass: Attribute automatisch berechnen
Verfasst: Dienstag 24. Juni 2025, 15:08
von __blackjack__
@Kebap: Da gibt es keinen Unterschied. Vielleicht wolltest Du fragen was effizienter ist?

Re: Dataclass: Attribute automatisch berechnen
Verfasst: Dienstag 24. Juni 2025, 15:57
von kbr
@Kebap: Wenn über einen zentralen Wert gegangen wird, dann hast du pro Abfrage maximal eine Rechenoperation. Beim setzen ebenso.
Re: Dataclass: Attribute automatisch berechnen
Verfasst: Dienstag 24. Juni 2025, 18:30
von DeaD_EyE
Über die Ausführungsgeschwindigkeit sollte man sich jedenfalls keine Gedanken machen

Die macht man sich z.B. erst, wenn man ein paar Millionen Objekte hat. Dann möchte man die Werte speichern, die zur Verfügung stehen bzw. in die entsprechende Einheit umrechnen. Aber bei so ein paar Objekten würde ich mir eher über die Wartung des Codes Gedanken machen und mögliche Seiteneffekte der Dataclass berücksichtigen. Je mehr Code, desto mehr wahrscheinliche Bugs.
Re: Dataclass: Attribute automatisch berechnen
Verfasst: Donnerstag 26. Juni 2025, 10:56
von Kebap
__blackjack__ hat geschrieben: Dienstag 24. Juni 2025, 15:08
@Kebap: Da gibt es keinen Unterschied. Vielleicht wolltest Du fragen was effizienter ist?
Ja.
kbr hat geschrieben: Dienstag 24. Juni 2025, 15:57
@Kebap: Wenn über einen zentralen Wert gegangen wird, dann hast du pro Abfrage maximal eine Rechenoperation. Beim setzen ebenso.
Wenn ich @noisefloor richtig verstand, dann soll im Web ein Wert eingegeben, und die resultierenden 4 anderen Werte ausgegeben werden. Also muss man die hier alle einmal umrechnen. OK, das muss man sowieso.
Die Frage ist ja eher, ob es eine andere Webseite gibt, auf der man regelmäßig alle 5 Werte gleichzeitig sichtbar machen will, ohne dass gerade einer geändert wurde und so alle neu berechnet werden mussten. Dann müsste man immer sehr viel rechnen, um diese Seite bloß anzuzeigen.
Vielleicht hat @Dead_Eye aber auch recht, und die Berechnungen werden die Geschwindigkeit der Anzeige kaum beeinflussen. Jedenfalls reduziert das den Speicherplatz und die Komplexität des Codes.
Re: Dataclass: Attribute automatisch berechnen
Verfasst: Donnerstag 26. Juni 2025, 14:13
von noisefloor
Hallo,
Danke für die Antworten und die Diskussion. Ich bin am Ende doch beim 1. Ansatz mit der dataclass und eine __post_init__ geblieben. Das sich alle Werte ändern sollen, wenn man eine aktualisiert, wäre interessant und nice-to-have, aber aktuell muss die erzeugte Instanz in der Tat nicht änderbar sein. Die "lebt" auch nur eine Request lang in einer Django View Funktion und ist dann Geschichte.
@__blackjack__: pint sieht interessant aus, schaue ich mir bei Gelegenheit mal an.
Gruß, noisefloor