@Sirius3 Oh danke, das ist eine super Idee das Rezept gleich in __init__ in die einzelnen Teile zu parsen, das macht alles viel überschaubarer und kann auch einfacher wieder verwendet werden, ohne dublizieren zu müssen.
Und ja genau, es geht darum die letzte Zeile mit einem * zu finden. Und sry, "ele" hatte ich als kürzere Form von "element" verwendet. Meine Version sieht ein bisschen "gebastelt" aus, ich habe join hinzugefügt, weil es alls Zeichen teilte, deswegen. Aber ich werde es wieder "entbasteln", sauberer machen.
Und danke, das mit dem zwei Mal speichern war mir vor unseren Diskussion gar nicht bewusst. Und super wegen "reversed" ich hatte .reverse() verwendet was nicht funktionierte und dann mit [::-1] ersetzt, aber stimmt, dass ist unnötig teuer. Danke dir. Und danke auch für deine Beispielcodes.
suche Ausgabe: int(List[index_of_character_*])
-
- User
- Beiträge: 42
- Registriert: Dienstag 13. April 2021, 11:54
"Those who can imagine anything, can create the impossible." Alan Turing
- __blackjack__
- User
- Beiträge: 13110
- Registriert: Samstag 2. Juni 2018, 10:21
- Wohnort: 127.0.0.1
- Kontaktdaten:
IMHO noch besser das Parsen nicht in der `__init__()` zu machen sondern in einer Funktion oder Klassenmethode. Dann kann man `Recipe`-Objekte auch aus anderen Quellen erstellen und/oder für Tests einfach so direkt erstellen, ohne das man das parsen immer mittesten muss.
Beispiel:
Das parsen ist wahrscheinlich noch nicht vollständig. Die Zubereitungszeit wird wahrscheinlich nicht nur in Stunden angegeben. Aus den Zutaten möchte man vielleicht die "*" entfernen den Rest vielleicht in Anzahl, Einheit, und Bezeichnung aufteilen und in einem eigenen Typ speichern. Und die Zubereitung vielleicht dann wieder als eine Zeichenkette, statt einzelnen Zeilen speichern.
Ausgabe von dem Beispiel:
Beispiel:
Code: Alles auswählen
#!/usr/bin/env python3
import re
from datetime import timedelta as TimeDelta
from attr import attrib, attrs
from more_itertools import split_when
EXAMPLE_RECIPE = """\
Preparation time: 1 hour
*\t5\t\tcarrots
*\t1\tpinches\tsalt
Bake for 40 minutes.
"""
PREPARATION_TIME_RE = re.compile(r"Preparation time: (?P<hour>\d+) hour")
@attrs(frozen=True)
class Recipe:
preparation_time = attrib()
ingredients = attrib()
instructions = attrib()
@classmethod
def parse(cls, text):
(
preparation_time_lines,
ingredient_lines,
instruction_lines,
) = split_when(
text.splitlines(),
lambda line_a, line_b: (
line_a.startswith("*") + line_b.startswith("*") == 1
),
)
if len(preparation_time_lines) != 1:
raise ValueError(
f"more than one line for preparation_time:"
f" {preparation_time_lines}"
)
match = PREPARATION_TIME_RE.fullmatch(preparation_time_lines[0])
if not match:
raise ValueError(
f"preparation time has unexpected format:"
f" {preparation_time_lines[0]!r}"
)
preparation_time = TimeDelta(hours=int(match.group("hour")))
# ...
return cls(preparation_time, ingredient_lines, instruction_lines)
def main():
recipe = Recipe.parse(EXAMPLE_RECIPE)
print(recipe)
for line in recipe.instructions:
print(line)
if __name__ == "__main__":
main()
Ausgabe von dem Beispiel:
Code: Alles auswählen
$ ./forum22.py
Recipe(preparation_time=datetime.timedelta(0, 3600), ingredients=['*\t5\t\tcarrots', '*\t1\tpinches\tsalt'], instructions=['Bake for 40 minutes.'])
Bake for 40 minutes.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Ich frage mich die ganze Zeit warum ihr das nicht mit einer list comprehension macht.
Habe ich etwas missverstanden?
Es geht doch nur darum, das nächste Listenelement nach der letzten Zeile, die mit '*' beginnt auszugeben, oder?
Habe ich etwas missverstanden?
Es geht doch nur darum, das nächste Listenelement nach der letzten Zeile, die mit '*' beginnt auszugeben, oder?
- __blackjack__
- User
- Beiträge: 13110
- Registriert: Samstag 2. Juni 2018, 10:21
- Wohnort: 127.0.0.1
- Kontaktdaten:
@rogerb: Ich vermute die *eine* Zeile ist nur als Beispiel und die Anweisungen sind länger. Wie würdest Du das mit einer list comprehension machen? Die sind dazu da um Listen zu erstellen. Hier soll ja aber eine Liste eher zerlegt werden.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Wenn es darum geht eine Liste nach einfachen Regeln in eine neue Liste umzuwandeln, währe mein erster Ansatz immer eine list comprehension, (bzw. analog generator expression oder dictionary comprehension)
Das geht wahrscheinlich noch in vielen weiteren Varianten mit itertools, usw.
nach etwas Überlegung, würde ich die Daten aber wohl eher in einen Dictionary parsen:
Da hätte ich dann viele Möglichkeiten die Daten weiterzuverarbeiten.
Das in etwa soll wohl auch deine Klasse anbieten. Ich persönlich würde den dictionary aber aufgrund seiner Einfachheit vorziehen.
Code: Alles auswählen
lines_after_ingredients = next(recipe[index+1:] for index, (prev_line, next_line) in enumerate(zip(recipe[:-1], recipe[1:])) if prev_line.startswith("*") and not next_line.startswith("*"))
nach etwas Überlegung, würde ich die Daten aber wohl eher in einen Dictionary parsen:
Code: Alles auswählen
recipe_dict = {"remarks": [], "ingredients": [], "instructions": []}
Das in etwa soll wohl auch deine Klasse anbieten. Ich persönlich würde den dictionary aber aufgrund seiner Einfachheit vorziehen.
@rogerb: man muß nicht alles in eine List-Comprehension packen, das ist nur dann sinnvoll, wenn es sich wirklich um einfache Ausdrücke handelt.
Du packst aber den vorhergehende und die nachfolgende Zeile zusammen mit einem Index in einen Ausdruck, wo eine einfache for-Schleife reichen würde.
Du packst aber den vorhergehende und die nachfolgende Zeile zusammen mit einem Index in einen Ausdruck, wo eine einfache for-Schleife reichen würde.
@Sirius3,
ja die Lesbarkeit bei dieser list comprehension, stößt vielleicht schon an die Grenze. Ich persönlich arbeite sehr viel mit ...-comprehensions. Daher währe das mein erster Ansatz.
Aber es ist dann auch irgendwo Geschmacksache.
Wenn es gute Argumente für eine extra Klasse gibt, lasse ich mich gerne überzeugen. Bisher sehe ich die halt nicht.
Es ging mir hier auch nicht darum irgend eine Lösung abzuliefern, sondern eher um die Argumente für die eine oder die andere Methode.
ja die Lesbarkeit bei dieser list comprehension, stößt vielleicht schon an die Grenze. Ich persönlich arbeite sehr viel mit ...-comprehensions. Daher währe das mein erster Ansatz.
Aber es ist dann auch irgendwo Geschmacksache.
Wenn es gute Argumente für eine extra Klasse gibt, lasse ich mich gerne überzeugen. Bisher sehe ich die halt nicht.
Es ging mir hier auch nicht darum irgend eine Lösung abzuliefern, sondern eher um die Argumente für die eine oder die andere Methode.
-
- User
- Beiträge: 42
- Registriert: Dienstag 13. April 2021, 11:54
@__blackjack__ @Sirius3 @rogerb Vielen Dank euch dreien für die weiteren Inputs Und ja die Anweisungen im Programm sind viel länger als nur eine Zeile.
Zu deinem Kommentar @__blackjack__ ist es nun besser das "Parsen des Rezeptteile" vor dem __init__() zu machen (also so wie unten im Pseudocode)? Ich komme noch nicht ganz draus, was dann aus der __init__ Methode wird. Fällt sie weg oder was kommt da rein, dann? Also so stelle ich es mir nun vor:
Und ja genau so sieht ein "verkürztes" example recipe aus, nur enthält sie noch weitere Zeilen wie beispielsweise eine Zeile Titel, eine Zeile Originaltitel, usw. und die hier 2. und 3. Zeile an Zutaten können beliebig viele sein. Die hier letzte Zeile, also Zeile 4, besteht wiederum aus mehreren beliebig vielen Anweisungen (hier auch nur eine Zeile zur Vereinfachung).
EXAMPLE_RECIPE = """\
Preparation time: 1 hour
*\t5\t\tcarrots
*\t1\tpinches\tsalt
Bake for 40 minutes.
"""
Zu deinem Kommentar @__blackjack__ ist es nun besser das "Parsen des Rezeptteile" vor dem __init__() zu machen (also so wie unten im Pseudocode)? Ich komme noch nicht ganz draus, was dann aus der __init__ Methode wird. Fällt sie weg oder was kommt da rein, dann? Also so stelle ich es mir nun vor:
Code: Alles auswählen
class...
#part1 = des geparsten Rezepts
#part2 = des geparste Rezepts
#...
def __init__(self):
#? was kommt dann noch im init? Nichts mehr?
def __iter__(self):
#Inhalt: siehe vorherige Nachrichten
def andere_funktionen(self):
#...
def main():
...
iter(a_receipe_name)
...
EXAMPLE_RECIPE = """\
Preparation time: 1 hour
*\t5\t\tcarrots
*\t1\tpinches\tsalt
Bake for 40 minutes.
"""
"Those who can imagine anything, can create the impossible." Alan Turing
Also, wenn die __init__() leer ist, frage ich mich, warum du nicht einen generator verwendest. Was spricht dann noch für eine Klasse?
Wahlweise (sorry, muss ich einfach nochmal sagen) könnte man die Zeilen nach all den verschiedenen Kategorien wie Titel, Originaltitel, usw in einen dictionary vorsortieren.
Wahlweise (sorry, muss ich einfach nochmal sagen) könnte man die Zeilen nach all den verschiedenen Kategorien wie Titel, Originaltitel, usw in einen dictionary vorsortieren.
@GabrielleChanel: __blackjack__ braucht kein __init__, weil er das attrs-Paket benutzt.
attrs erzeugt automatisch ein __init__, das so aussehen würde:
attrs erzeugt automatisch ein __init__, das so aussehen würde:
Code: Alles auswählen
class Recipe:
def __init__(self, preparation_time, ingredients, instructions):
self.preparation_time = preparation_time
self.ingredients = ingredients
self.instructions = instructions
def iter_instructions(self):
return iter(self.instructions)
-
- User
- Beiträge: 42
- Registriert: Dienstag 13. April 2021, 11:54
@Sirius3 Vielen herzlichen Dank, das wusste ich nicht mit den attr(), die selbst schon ein __init__() aufrufen. Stimmt, dann kann man diese ja weglassen, statt nochmals aufzuschreiben. Jetzt gibt alles Sinn, vielen Dank
"Those who can imagine anything, can create the impossible." Alan Turing