In dem `input()` das am Anfang gehört da nicht rein — einzelne Variablennamen sollte auch `evaluate()` ganz normal auswerten können, das ist kein Sonderfall der vorher abgearbeitet werden muss.
Das `Assign` gehört auch ganz normal als ``case`` in die `evaluate()`-Methode.
Bei `Name` ist der Code unnötig kompliziert. Die Konstante erstellen und auswerten macht keinen Sinn.
CPython vs. Numba vs. Rust
- __blackjack__
- User
- Beiträge: 14044
- Registriert: Samstag 2. Juni 2018, 10:21
- Wohnort: 127.0.0.1
- Kontaktdaten:
„A life is like a garden. Perfect moments can be had, but not preserved, except in memory. LLAP” — Leonard Nimoy's last tweet.
Danke, ich glaube das habe ich soweit verstanden.
Nur wenn ich etwas wie "x=1;y=2" dem Interpreter gebe, komme ich nicht weiter.
Ich sehe, dass `body` in `Interactive` dann eine Liste mit zwei `Assign`-Objekten hat. Dachte darüber iteriere ich einfach, aber so einfach ist es nicht. Brauche hier leider doch Hilfe.
Aktueller Stand:
Grüße
Dennis
Nur wenn ich etwas wie "x=1;y=2" dem Interpreter gebe, komme ich nicht weiter.
Ich sehe, dass `body` in `Interactive` dann eine Liste mit zwei `Assign`-Objekten hat. Dachte darüber iteriere ich einfach, aber so einfach ist es nicht. Brauche hier leider doch Hilfe.
Aktueller Stand:
Code: Alles auswählen
#!/usr/bin/env python
from ast import (Add, Assign, BinOp, Constant, Div, Expr, Interactive, Mod,
Mult, Name, Sub, UnaryOp, USub, parse, dump)
from operator import add, mod, mul, neg, sub, truediv
OPERATOR_TYPE_TO_FUNCTION = {
Add: add,
Mult: mul,
Sub: sub,
USub: neg,
Div: truediv,
Mod: mod,
}
class Interpreter:
def __init__(self):
self.variables = {}
def input(self, expression):
if expression.isspace() or not expression:
return ""
node = parse(expression, mode="single")
return self.evaluate(node)
def evaluate(self, node):
match node:
case BinOp(left=left, op=op, right=right):
return OPERATOR_TYPE_TO_FUNCTION[type(op)](
self.evaluate(left), self.evaluate(right)
)
case Constant(value=value):
return value
case Expr(value=body):
return self.evaluate(body)
case UnaryOp(op=op, operand=operand):
return OPERATOR_TYPE_TO_FUNCTION[type(op)](self.evaluate(operand))
case Interactive(body=body):
for assign in body:
print(dump(assign, indent=4))
return self.evaluate(assign)
case Assign(value=value):
self.variables[node.targets[0].id] = self.evaluate(value)
return self.variables[node.targets[0].id]
case Name(id=id):
return self.variables[id]
case _:
raise AssertionError(node)
def main():
interpreter = Interpreter()
# assert interpreter.input("1 + 1") == 2
# assert interpreter.input("2 - 1") == 1
# assert interpreter.input("2 * 3") == 6
# assert interpreter.input("8 / 4") == 2
# assert interpreter.input("7 % 4") == 3
# assert interpreter.input("x = 1") == 1
# assert interpreter.input("x") == 1
# assert interpreter.input("x + 3") == 4
# assert interpreter.input(" ") == ""
# assert interpreter.input("") == ""
print(interpreter.input("x=1+1;y=2+3"))
#print(interpreter.input("x"))
#print(interpreter.input("y"))
print("Done!")
if __name__ == "__main__":
main()
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Doch, so einfach ist es. Guck dir nochmal genau deine `for`-Schleife an, die ... schleift nicht so richtig?Dennis89 hat geschrieben: Sonntag 17. November 2024, 16:41 Dachte darüber iteriere ich einfach, aber so einfach ist es nicht. Brauche hier leider doch Hilfe.
Danke für die Antwort.
Okay, `return` war doof platziert.
Was haltet ihr davon?
Ist ne `tuple` üblich oder was würde man für eine Datenstruktur wählen?
Grüße
Dennis
Okay, `return` war doof platziert.
Was haltet ihr davon?
Code: Alles auswählen
#!/usr/bin/env python
from ast import (Add, Assign, BinOp, Constant, Div, Expr, Interactive, Mod,
Mult, Name, Sub, UnaryOp, USub, parse)
from operator import add, mod, mul, neg, sub, truediv
OPERATOR_TYPE_TO_FUNCTION = {
Add: add,
Mult: mul,
Sub: sub,
USub: neg,
Div: truediv,
Mod: mod,
}
class Interpreter:
def __init__(self):
self.variables = {}
def input(self, expression):
if expression.isspace() or not expression:
return ""
node = parse(expression, mode="single")
return self.evaluate(node)
def evaluate(self, node):
match node:
case BinOp(left=left, op=op, right=right):
return OPERATOR_TYPE_TO_FUNCTION[type(op)](
self.evaluate(left), self.evaluate(right)
)
case Constant(value=value):
return value
case Expr(value=body):
return self.evaluate(body)
case UnaryOp(op=op, operand=operand):
return OPERATOR_TYPE_TO_FUNCTION[type(op)](self.evaluate(operand))
case Interactive(body=body):
return (
tuple([self.evaluate(assign) for assign in body])
if len(body) > 1
else self.evaluate(*body)
)
case Assign(value=value):
self.variables[node.targets[0].id] = self.evaluate(value)
return self.variables[node.targets[0].id]
case Name(id=id):
return self.variables[id]
case _:
raise AssertionError(node)
def main():
interpreter = Interpreter()
assert interpreter.input("1 + 1") == 2
assert interpreter.input("2 - 1") == 1
assert interpreter.input("2 * 3") == 6
assert interpreter.input("8 / 4") == 2
assert interpreter.input("7 % 4") == 3
assert interpreter.input("x = 1") == 1
assert interpreter.input("x") == 1
assert interpreter.input("x + 3") == 4
assert interpreter.input(" ") == ""
assert interpreter.input("") == ""
print(interpreter.input("x=1+1;y=2+3"))
print(interpreter.input("x"))
print(interpreter.input("y"))
print("Done!")
if __name__ == "__main__":
main()
Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
- __blackjack__
- User
- Beiträge: 14044
- Registriert: Samstag 2. Juni 2018, 10:21
- Wohnort: 127.0.0.1
- Kontaktdaten:
Das kannst Du doch im Grunde selbst festlegen. Python macht folgendes:
IPython macht was anderes:
Ich persönlich bin nicht so der Fan von Funktionen die unterschiedliche Dinge zurückgeben. Also hier neben einer Zahl oder einem Tupel ja auch noch eine leere Zeichenkette. 
Ich würde einfach immer eine Liste zurückgeben. Leer wenn nix eingegeben wurde, und ansonsten mit der Anzahl der Ergebnisse für die Elemente der Eingabeliste.
Was Python und IPython gleich machen: `None` wird einfach nicht ausgegeben.
Code: Alles auswählen
>>> 23
23
>>> 23; 42
23
42
Code: Alles auswählen
In [402]: 23
Out[402]: 23
In [403]: 23; 42
Out[403]: 42
Ich würde einfach immer eine Liste zurückgeben. Leer wenn nix eingegeben wurde, und ansonsten mit der Anzahl der Ergebnisse für die Elemente der Eingabeliste.
Was Python und IPython gleich machen: `None` wird einfach nicht ausgegeben.
Code: Alles auswählen
>>> None
>>>
Code: Alles auswählen
In [404]: None
In [405]:
„A life is like a garden. Perfect moments can be had, but not preserved, except in memory. LLAP” — Leonard Nimoy's last tweet.
Die leere Zeichenkette war in der Aufgabenstellung vorgegeben. Die Tuple gefällt mir besser als eine Liste, dann lasse ich das jetzt so.
Puuuh, jetzt wäre ich reif für das Wochenende
Vielen Dank, für eure Hilfe! War mal wieder sehr lehrreich.
Grüße
Dennis
Puuuh, jetzt wäre ich reif für das Wochenende
Vielen Dank, für eure Hilfe! War mal wieder sehr lehrreich.
Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Hallo,
ihr hattet hier meinen Kopf mit "Visitor-Pattern" ziemlich qualmen gebracht. Ich arbeite ja immer wieder an Berechnungsprogrammen und es kam der Wunsch bestimmte Werte in einer Liste zu haben. Ich weis noch nicht, ob das so sinnvoll ist, aber unabhängig davon und viel mehr aus Interesse, wäre das ein sinnvoller Einsatz für Visitor-Pattern?
Gegeben ist mal folgende Struktur:
Die Anzahl der Liste in `end_temperatures` ist gleich der Anzahl der Liste `cylinders`. Die können aber je Anwendungsfall von 1 bis theoretisch unendlich, variieren.
Angenommen wir haben 3 Zylindern, dann sollte die gewünschte Liste (oder auch ein anderes Objekt) folgendes beinhalten:
Kann man hier jetzt so ein Besucher bauen, der durch die Klassen geht und sich die Werte abholt? Oder sind hier andere Methoden sinnvoller bzw. was wäre am sinnvollsten?
Danke und Grüße
Dennis
ihr hattet hier meinen Kopf mit "Visitor-Pattern" ziemlich qualmen gebracht. Ich arbeite ja immer wieder an Berechnungsprogrammen und es kam der Wunsch bestimmte Werte in einer Liste zu haben. Ich weis noch nicht, ob das so sinnvoll ist, aber unabhängig davon und viel mehr aus Interesse, wäre das ein sinnvoller Einsatz für Visitor-Pattern?
Gegeben ist mal folgende Struktur:
Code: Alles auswählen
#!/usr/bin/env python
from attr import define, field, make_class
from attrs.validators import (
gt as is_greater,
min_len,
)
Results = make_class(
"Results",
[
"end_temperatures",
]
)
@define()
class Cylinder:
inlet_temperature = field(validator=is_greater(273.15))
@define
class Motor:
cylinders: list[Cylinder] = field(validator=min_len(1))
final_temperature = field(validator=is_greater(0))
@define
class Car:
motor: Motor = field()
def main():
...
if __name__ == "__main__":
main()
Angenommen wir haben 3 Zylindern, dann sollte die gewünschte Liste (oder auch ein anderes Objekt) folgendes beinhalten:
Code: Alles auswählen
"Endtemperatur Zylinder 1", "Einlasstemperatur Zylinder 2", "Endtemperatur Zylinder 2", "Einlasstemperatur Zylinder 3", "Endtemperatur Zylinder 3", "Endtemperatur Motor"
Danke und Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
- __blackjack__
- User
- Beiträge: 14044
- Registriert: Samstag 2. Juni 2018, 10:21
- Wohnort: 127.0.0.1
- Kontaktdaten:
@Dennis89: Ich verstehe hier nicht wie diese Liste konkret zustande kommen soll und wo man hier einen Besucher braucht? Nehmen wir mal als Einstiegspunkt eine Funktion — die bekommt was als Argument? Ein Auto? Sähe die dann nicht einfach so aus?
Code: Alles auswählen
def calculate_something(car):
results = []
for cylinder in car.motor.cylinders:
# do some calculations
results.append(result_of_calculations)
return results
„A life is like a garden. Perfect moments can be had, but not preserved, except in memory. LLAP” — Leonard Nimoy's last tweet.
Guten Morgen,
ja die Berechnung sieht so ähnlich aus. Da habe ich mich undeutlich ausgedrückt. Nehmen wir an der Rechenvorgang für Motor und Zylinder ist abgeschlossen. Dann habe ich in den Motor- und Zylinderklassen meine Eingabewerte ("Endtemperatur Motor", "Einlasstemperatur Zylinder 1-3" (die 1. wird in der Liste nicht benötigt)) und in der Ergebnisklasse habe ich die anderen Temperaturen. Der Fall sieht jetzt so aus, dass für das Auto ein weiteres Objekt berechnet werden soll, das genau von diesen Temperaturen abhängig ist. Ob das jetzt sinnvoll ist, dass die alle so in einer Liste stehen, weiß ich noch nicht, aber beim überlegen wie ich sinnvoll an die Werte komme ohne mich auf eine bestimmte Zylinderanzahl festzulegen, dachte ich, es wäre ganz geschickt wenn da einer die Klassen besuchen würde.
Ich kann auch eine Funktion schreiben und mir die Werte einzeln aus den Objekten holen, so würde ich es machen, wenn ihr sagt Visitor-Pattern ist hier Quatsch.
Danke und Grüße
Dennis
ja die Berechnung sieht so ähnlich aus. Da habe ich mich undeutlich ausgedrückt. Nehmen wir an der Rechenvorgang für Motor und Zylinder ist abgeschlossen. Dann habe ich in den Motor- und Zylinderklassen meine Eingabewerte ("Endtemperatur Motor", "Einlasstemperatur Zylinder 1-3" (die 1. wird in der Liste nicht benötigt)) und in der Ergebnisklasse habe ich die anderen Temperaturen. Der Fall sieht jetzt so aus, dass für das Auto ein weiteres Objekt berechnet werden soll, das genau von diesen Temperaturen abhängig ist. Ob das jetzt sinnvoll ist, dass die alle so in einer Liste stehen, weiß ich noch nicht, aber beim überlegen wie ich sinnvoll an die Werte komme ohne mich auf eine bestimmte Zylinderanzahl festzulegen, dachte ich, es wäre ganz geschickt wenn da einer die Klassen besuchen würde.
Ich kann auch eine Funktion schreiben und mir die Werte einzeln aus den Objekten holen, so würde ich es machen, wenn ihr sagt Visitor-Pattern ist hier Quatsch.
Danke und Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Ich glaube das wird so nicht wirklich sinnvoll bzw. das geht auch am Gedanke der Methode vorbei.
Hab das mal so hinbekommen, dass da was aus den Klassen geholt wird, aber wenn dann soll das ja in der Reihenfolge wie ich es benötige geholt werden und dann eine Liste als Ergebnis bringen. Bin ich mit dem Konzept hier falsch?
Das ist mein Versuch
Grüße und Danke
Dennis
Hab das mal so hinbekommen, dass da was aus den Klassen geholt wird, aber wenn dann soll das ja in der Reihenfolge wie ich es benötige geholt werden und dann eine Liste als Ergebnis bringen. Bin ich mit dem Konzept hier falsch?
Das ist mein Versuch
Code: Alles auswählen
#!/usr/bin/env python
from attr import define, field
@define
class Visitor:
@staticmethod
def visit_any():
return None
def visit(self, node):
return getattr(self, f"visit_{node.__class__.__name__}", self.visit_any)()
class Results(Visitor):
def __init__(self):
self.outlet_temperatures = [200, 300, 400]
def visit_Results(self):
return self.outlet_temperatures[1:]
@define()
class Cylinder:
inlet_temperature = field()
@define
class Motor(Visitor):
cylinders: list[Cylinder] = field()
final_temperature = field()
def visit_Motor(self):
temperatures = [cylinder.inlet_temperature for cylinder in self.cylinders]
temperatures.append(self.final_temperature)
return temperatures
@define
class Car:
motor: Motor = field()
def main():
car = Car(Motor([Cylinder(999), Cylinder(888)], 6666))
results = Results()
print(results.visit(results))
print(car.motor.visit(car.motor))
if __name__ == "__main__":
main()
Grüße und Danke
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
- __blackjack__
- User
- Beiträge: 14044
- Registriert: Samstag 2. Juni 2018, 10:21
- Wohnort: 127.0.0.1
- Kontaktdaten:
@Dennis89: Hier hast Du jetzt einen Besucher der sich selbst besucht, und ausschliesslich sich selbst besucht und auch nur besuchen kann. Das macht keinen Sinn.
Welches Problem möchtest Du denn überhaupt mit dem Besucher-Entwurfsmuster lösen? Wie löst Du das ohne dieses Muster, welches Problem entsteht dabei, und wie löst das Entwurfsmuster dieses Problem? Das ist so ein typischer ”Fehler” bei Leuten die Entwurfsmuster lernen: die haben die Muster und suchen dann nach einem Problem dafür, und es entsteht eine Menge unnötiger Code. Richtig ist es erst ein Problem zu haben, und dafür nach einem Entwurfsmuster zu suchen.
Welches Problem möchtest Du denn überhaupt mit dem Besucher-Entwurfsmuster lösen? Wie löst Du das ohne dieses Muster, welches Problem entsteht dabei, und wie löst das Entwurfsmuster dieses Problem? Das ist so ein typischer ”Fehler” bei Leuten die Entwurfsmuster lernen: die haben die Muster und suchen dann nach einem Problem dafür, und es entsteht eine Menge unnötiger Code. Richtig ist es erst ein Problem zu haben, und dafür nach einem Entwurfsmuster zu suchen.
„A life is like a garden. Perfect moments can be had, but not preserved, except in memory. LLAP” — Leonard Nimoy's last tweet.
Danke für deine Antwort.
Danke und Grüße
Dennis
Ich war mir von Anfang an nicht sicher, ob Visitor-Pattern hier sinnvoll ist, deswegen schrieb ich:Das ist so ein typischer ”Fehler” bei Leuten die Entwurfsmuster lernen: die haben die Muster und suchen dann nach einem Problem dafür,
Ich weis noch nicht, ob das so sinnvoll ist, aber unabhängig davon und viel mehr aus Interesse, wäre das ein sinnvoller Einsatz für Visitor-Pattern?
Ich wollte die `temperatures` - Liste in der `Cooler`-Klasse mit Visitor-Pattern erstellen. Ohne das Entwurfsmuster würde ich es wie so wie in `collect_temperatures` lösen. Da habe ich aber noch das Problem, dass `Cooler` abhängig von den berechneten Werten in `Motor` ist und daher erst nach der Berechnung instanziert werden kann und sich deshalb noch nicht in der `Car`-Klasse befindet. Da sollte er aber meiner Meinung nach schon rein und deshalb schrieb ich weiter oben auch, das ich noch nicht weis ob die Liste, die erstellt werden soll, so sinnvoll ist. Aber davon können wir ja mal absehen und einfach annehmen, es wäre sinnvoll.Welches Problem möchtest Du denn überhaupt mit dem Besucher-Entwurfsmuster lösen?
Code: Alles auswählen
#!/usr/bin/env python
from attr import define, field
from itertools import zip_longest
@define
class Results:
outlet_temperatures = field(default=[200, 300])
@define
class Cylinder:
inlet_temperature = field()
@define
class Motor:
cylinders: list[Cylinder] = field()
final_temperature = field()
@define
class Cooler:
temperatures = field()
@classmethod
def collect_temperatures(cls, motor, results):
cylinders_inlet_temperatures = [
cylinder.inlet_temperature for cylinder in motor.cylinders
][1:]
return cls(
list(
zip_longest(
results.outlet_temperatures,
cylinders_inlet_temperatures,
fillvalue=motor.final_temperature,
)
),
)
@define
class Car:
motor: Motor = field()
def main():
car = Car(Motor([Cylinder(999), Cylinder(888)], 6666))
results = Results()
cooler = Cooler.collect_temperatures(car.motor, results)
print(cooler.temperatures)
if __name__ == "__main__":
main()
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
- __blackjack__
- User
- Beiträge: 14044
- Registriert: Samstag 2. Juni 2018, 10:21
- Wohnort: 127.0.0.1
- Kontaktdaten:
Also ich verstehe den Code nicht. Das ist das Problem an so abstrakten Beispielen, da kann man jetzt alles oder nichts verändern, man weiss am Ende nicht ob das dann auch bei einem realen Code funktioniert oder nicht. Einiges an Entscheidungen was zu welchem Objekt gehört und was nicht, hängt ja von der Bedeutung ab, und hier gibt es nicht wirklich eine.
Das Beispiel könnte man ja beispielsweise als erstes mal auf das hier reduzieren/vereinfachen, ohne das hier wirklich etwas verloren geht:
Das Beispiel könnte man ja beispielsweise als erstes mal auf das hier reduzieren/vereinfachen, ohne das hier wirklich etwas verloren geht:
Code: Alles auswählen
#!/usr/bin/env python
from attr import define, field
@define
class Motor:
cylinder_inlet_temperatures = field()
final_temperature = field()
def collect_temperatures(motor, outlet_temperatures):
if len(motor.cylinder_inlet_temperatures) != len(outlet_temperatures):
raise ValueError(
"length of inlet and outlet temperatures doesn't match"
)
return list(
zip(
outlet_temperatures,
motor.cylinder_inlet_temperatures[1:] + [motor.final_temperature],
)
)
def main():
motor = Motor([999, 888], 6666)
outlet_temperatures = [200, 300]
temperatures = collect_temperatures(motor, outlet_temperatures)
print(temperatures)
if __name__ == "__main__":
main()
„A life is like a garden. Perfect moments can be had, but not preserved, except in memory. LLAP” — Leonard Nimoy's last tweet.
Danke für die Antwort.
Ja da hast du wohl recht. Vielleicht kann man das besser darstellen, wenn das Programm mal lauffähig steht. An sich ist das von der Art her nicht viel anders, als meine andere Web-App, "nur" etwas umfangreichere Auswahl- und Berechnungsmöglichkeiten. Ich denke zu einem lauffähigen Ergebnis sollte ich schon kommen, ich orientiere mich da stark an euren Vorschlägen im anderen Thread.
Aber um was es eigentlich hier ging, Visitor-Pattern zu verwenden, ich behalte es weiter im Hinterkopf und melde mich wieder, wenn ich wieder denke, dass ich es anwenden könnte
Grüße
Dennis
Ja da hast du wohl recht. Vielleicht kann man das besser darstellen, wenn das Programm mal lauffähig steht. An sich ist das von der Art her nicht viel anders, als meine andere Web-App, "nur" etwas umfangreichere Auswahl- und Berechnungsmöglichkeiten. Ich denke zu einem lauffähigen Ergebnis sollte ich schon kommen, ich orientiere mich da stark an euren Vorschlägen im anderen Thread.
Aber um was es eigentlich hier ging, Visitor-Pattern zu verwenden, ich behalte es weiter im Hinterkopf und melde mich wieder, wenn ich wieder denke, dass ich es anwenden könnte
Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]