
Code-Review zur Thematik "Konstanten"
- __blackjack__
- User
- Beiträge: 14028
- Registriert: Samstag 2. Juni 2018, 10:21
- Wohnort: 127.0.0.1
- Kontaktdaten:
@Buchfink: Für *den* Einsatzzweck ist es egal ob Du `istitle()` oder ein Gegenstück zu `capitalize()` verwendest, denn es geht nur um ein Wort. Da unterscheiden sich `title()` und `capitalize()` nicht. Bei mehr als einem Wort macht es aber einen Unterschied:
`main()` definiere ich, wie wohl die allermeisten anderen auch, als letztes, nicht nur aus Gewohnheit beispielsweise aus C oder Pascal, sondern auch aus Konsistenzgründen.
Man kann ja auch der Meinung sein, dass man erst das ”grosse, grobe Bild malt” und die kleineren detaillierteren Teile danach ausfüllt. Also von der Hauptfunktion startend, und dann die Einzelschritte, bis zu Detailaufgaben und Hilfsfunktionen runter, der Quelltext im Laufe der Datei immer spezieller wird. Das Problem bei Python ist, dass man das nicht sauber durchhalten kann, weil das nicht in statische Programmanalyse zuerst und dynamische Laufzeit danach aufgeteilt ist, sondern auch der Modulinhalt ausführbarer Code ist, und alles was man verwendet, vorher definiert sein muss. Und zumindest alles was man für das ausführen von ``class``- und ``def``-Anweisungen benötigt, muss vorher definiert worden sein, auch wenn es kleinere Details sind.
Das betrifft beispielsweise Dekoratoren und alles was man so für Defaultargumente braucht. Also beispielsweise auch Funktionen oder Klassen.
Und dann gibt es auch noch Fälle wo Funktionen nicht per ``def`` definiert werden, sondern aus anderen Funktionen erstellt werden, die dafür vorher aber definiert worden sein müssen, auch wenn das nur Hilfsfunktionen sind, die nicht einmal zur öffentlichen API gehören.
Also beispielsweise würde man ja für eine Cäsar-Chiffre `encode()` und `decode()` vor einer Hilfsfunktion definieren, wenn man vom groben, zum Detail gehen würde. Das kann man machen, wenn man `encode()` und `decode()` tatsächlich als Funktionen hin schreiben will, aber nicht wenn man die beiden Funktionen aus der Hilfsfunktion + Parameter erstellt.
Also das hier geht:
Aber das hier ginge nicht:
Da müsste man dann anfangen gegen die selbst gesteckten Reihenfolgeregeln zu verstossen, oder man schränkt sich freiwillig ein, bestimmte Spracheigenschaften und Möglichkeiten nicht zu nutzen.
Code: Alles auswählen
In [380]: "foo bar".title()
Out[380]: 'Foo Bar'
In [381]: "foo bar".capitalize()
Out[381]: 'Foo bar'
Man kann ja auch der Meinung sein, dass man erst das ”grosse, grobe Bild malt” und die kleineren detaillierteren Teile danach ausfüllt. Also von der Hauptfunktion startend, und dann die Einzelschritte, bis zu Detailaufgaben und Hilfsfunktionen runter, der Quelltext im Laufe der Datei immer spezieller wird. Das Problem bei Python ist, dass man das nicht sauber durchhalten kann, weil das nicht in statische Programmanalyse zuerst und dynamische Laufzeit danach aufgeteilt ist, sondern auch der Modulinhalt ausführbarer Code ist, und alles was man verwendet, vorher definiert sein muss. Und zumindest alles was man für das ausführen von ``class``- und ``def``-Anweisungen benötigt, muss vorher definiert worden sein, auch wenn es kleinere Details sind.
Das betrifft beispielsweise Dekoratoren und alles was man so für Defaultargumente braucht. Also beispielsweise auch Funktionen oder Klassen.
Und dann gibt es auch noch Fälle wo Funktionen nicht per ``def`` definiert werden, sondern aus anderen Funktionen erstellt werden, die dafür vorher aber definiert worden sein müssen, auch wenn das nur Hilfsfunktionen sind, die nicht einmal zur öffentlichen API gehören.
Also beispielsweise würde man ja für eine Cäsar-Chiffre `encode()` und `decode()` vor einer Hilfsfunktion definieren, wenn man vom groben, zum Detail gehen würde. Das kann man machen, wenn man `encode()` und `decode()` tatsächlich als Funktionen hin schreiben will, aber nicht wenn man die beiden Funktionen aus der Hilfsfunktion + Parameter erstellt.
Also das hier geht:
Code: Alles auswählen
def encode(text, key):
return _code(add, text, key)
def decode(text, key):
return _code(sub, text, key)
def _code(offset_function, text, key):
...
Code: Alles auswählen
encode = partial(_code, add)
decode = partial(_code, sub)
def _code(offset_function, text, key):
...
„A life is like a garden. Perfect moments can be had, but not preserved, except in memory. LLAP” — Leonard Nimoy's last tweet.
@__blackjack__
lieben Dank und gut zu wissen, denn dann wäre mein vorheriger Funktionsname ohnehin auch noch semantisch falsch/verwirrend gewesen.
hier mal der gesamte Code, wie ich ihn nun habe:
Auszug Sprueche.txt
Ich denke, dass das wohl eine pythonische Reihenfolge ist - korrekt?
Am Rande:
Der Code tut was er soll. Aber Ich bin trotzdem ein bisschen unglücklich damit. Hier mal eine Liste all meiner Unglücksfaktoren
1) Ich lese aus einer Datei. Für ein Code-Review ist das blöd, denn man kann den Code dann nicht einfach mal ausprobieren. Ich möchte hier wenn's geht, keine riesigen Aufwände generieren. Dieser Code in investigate_sayings() ist daher schlecht.
Verbesserungsvorschlag: direkt aus Wikipedia auslesen. Ich bin hier aber noch am Recherchieren wie das genau geht und melde mich ggfls. mit konkreten Fragen
2) Das Datenmodell gefällt mir nicht. Es fühlt sich falsch an, einen Spruch als "key" zu missbrauchen. Ein Key mit Leerzeichen oder Umlauten ist irgendwie schräg.
Verbesserungsvorschlag: Klassen, die die Daten halten.
Was meint ihr so?
3) hardcodierte Reim-Zuordnung. Tja nun
Es ist nur ein Prototyp und ich wollte erst mal sehen, ob das überhaupt eine gute Idee ist. Letztlich musss da aber was generisches hin.
4) Algorithmus zur Substitution: hier könnte man auch flexibler werden und vielleicht mal gucken wollen, ob man statt Reime Synonyme oder Antonyme durchprobiert.
Hier mangelt es an Flexibilität aus meiner Sicht.
Verbesserungsvorschlag: auf ein Klassenmodell umziehen und dann mittels Polymorphie (oder Funktion als Parameter?)
5) Algorithmus zum Finden interessanter Wörter: könnte man auch für Verben haben wollen. Nomen sind halt leichter heuristisch einigermaßen erkennbar, daher sind es im ersten Wurf Nomen geworden.
6) Main: könnte man auch mit einem Einzeiler schreiben.
Zum debuggen finde ich es manchmal praktischer, wenn es nicht ganz so kompakt steht. Was meint ihr denn?
7) siehe TODOs im Code selbst.
continue.... tja nun... ich bin unentschlossen. Ich mag "continue" eigentlich gar nicht. Besser in zwei Funktionen splitten? Ein If erhöht halt wiederum die Verschachtelungstiefe. Was meint Ihr?
Es gibt noch einen Haufen Aspekte, die man verbessern könnte. Ich poste den Code hier mal, damit Ihr einen Eindruck bekommt, was ich so mache.
Ich freue mich auf Euer Feedback!
LG
versteheBei mehr als einem Wort macht es aber einen Unterschied:

ja genau, das schlägt Hr. Martin vor.Man kann ja auch der Meinung sein, dass man erst das ”grosse, grobe Bild malt” und die kleineren detaillierteren Teile danach ausfüllt. Also von der Hauptfunktion startend, und dann die Einzelschritte, bis zu Detailaufgaben und Hilfsfunktionen runter, der Quelltext im Laufe der Datei immer spezieller wird.
hier mal der gesamte Code, wie ich ihn nun habe:
Auszug Sprueche.txt
Code: Alles auswählen
A
"Abends werden die Faulen fleissig." - Citatboken, Bokförlaget Natur och Kultur, Stockholm, 1967, ISBN 91-27-01681-1
"Abwarten und Tee trinken." - Wander-DSL, Bd. 5, Sp. 702, commons. (Dort zitiert als: "Abwarten und Theetrinken.")
"Adel verpflichtet." (Noblesse oblige) - nach Pierre-Marc-Gaston de Lévis, Maximes et réflections
"Alle Menschen in der Welt streben nur nach Gut und Geld; und wenn sie es dann erwerben legen sie sich hin und sterben." - Citatboken, Bokförlaget Natur och Kultur, Stockholm, 1967, ISBN 91-27-01681-1
"Alle Sünden in eine münden."
"Alle Wege führen nach Rom." - Wander-DSL, Bd. 4, Sp. 1842, commons
"Aller Anfang ist schwer." - Wander-DSL, Bd. 1, Sp. 80, commons
"Aller guten Dinge sind drei." - Wander-DSL, Bd. 1, Sp. 605, commons. (Dort zitiert als: "Aller guten Ding seynd drey.")
"Alles Gute kommt von oben."
"Alles hat seine Zeit, nur die alten Weiber nicht." - Wander-DSL, Bd. 1, Sp. 46, commons
"Alles neu macht der Mai." - nach dem Gedicht von Hermann Adam von Kamp "Alles neu, macht der Mai" (1818)
Code: Alles auswählen
from pathlib import Path
def investigate_sayings():
# TODO
# Verbesserung mittels wiki-api - Texte direkt aus Wikipedia "Sprichwörter" auslesen?
# Methode "tut" zu viel - Zerlegen in Beschaffung der Daten und Erstellen des Datenmodells
filename = Path("C:/Users/Buchfink/Documents/Projkete/WortspielGenerator/Sprueche.txt")
with open(filename, "r", encoding="UTF-8") as file:
investigated_sayings = {}
for line in file:
splitted = line.strip().split(".\"")
saying = splitted[0].strip().replace('\"', '')
if len(saying) == 1:
continue
words = saying.split(" ")
interesting_words = []
for word in words:
if word.istitle():
interesting_words.append(word)
investigated_sayings[saying] = interesting_words
return investigated_sayings
def get_rhyme(word):
# TODO
# Quelle ist noch unklar. Daher nur mal exemplarische simplifizierte Beispielzuordnung
# Ungelöste Probleme:
# - Quelle selbst, hardcodiert ist ja keine Lösung :)
# - Artikelproblem - gleicher Artikel funktioniert immer, unterschiedliche Artikel eher weniger (kontextabhängig)
# - es gibt ggf. mehrere Reimwörter pro Wort,
# - Plural vs. Singular
# - Beidseitigkeit - sprich Laus reimt sich auf Maus und umgekehrt. Man könnte vereinfachen, indem man
# jedes Pärchen nur einmal einfügt
rhymes = {
"Esel": "Sessel", "Sessel": "Esel",
"Geld": "Held", "Held": "Geld",
"Spiegel": "Igel", "Igel": "Spiegel",
"Kopf": "Topf", "Topf": "Kopf",
"Baum": "Raum", "Raum": "Baum",
"Berg": "Zwerg", "Zwerg": "Berg",
"Laus": "Maus", "Maus": "Laus",
"Adel": "Sargnagel", "Sargnagel": "Adel",
"Wunsch": "Punsch", "Punsch": "Wunsch",
"Flügel": "Kleiderbügel",
"Waffen": "Karaffen",
"Macht": "Verdacht"
}
rhyme = rhymes.get(word)
return rhyme
def substitute_with_rhymes(investigated_sayings):
substituted_sayings = []
for key, value in investigated_sayings.items():
for word in value:
substituted_word = get_rhyme(word)
if substituted_word is not None:
substituted_sayings.append(key.replace(word, substituted_word))
return substituted_sayings
def show_sayings(sayings):
for saying in sayings:
print(saying)
print(f"Anzahl {len(sayings)}")
def main():
investigated = investigate_sayings()
substituted = substitute_with_rhymes(investigated)
show_sayings(substituted)
if __name__ == '__main__':
main()
Am Rande:
Der Code tut was er soll. Aber Ich bin trotzdem ein bisschen unglücklich damit. Hier mal eine Liste all meiner Unglücksfaktoren
1) Ich lese aus einer Datei. Für ein Code-Review ist das blöd, denn man kann den Code dann nicht einfach mal ausprobieren. Ich möchte hier wenn's geht, keine riesigen Aufwände generieren. Dieser Code in investigate_sayings() ist daher schlecht.
Verbesserungsvorschlag: direkt aus Wikipedia auslesen. Ich bin hier aber noch am Recherchieren wie das genau geht und melde mich ggfls. mit konkreten Fragen
2) Das Datenmodell gefällt mir nicht. Es fühlt sich falsch an, einen Spruch als "key" zu missbrauchen. Ein Key mit Leerzeichen oder Umlauten ist irgendwie schräg.
Verbesserungsvorschlag: Klassen, die die Daten halten.
Was meint ihr so?
3) hardcodierte Reim-Zuordnung. Tja nun

4) Algorithmus zur Substitution: hier könnte man auch flexibler werden und vielleicht mal gucken wollen, ob man statt Reime Synonyme oder Antonyme durchprobiert.

Hier mangelt es an Flexibilität aus meiner Sicht.
Verbesserungsvorschlag: auf ein Klassenmodell umziehen und dann mittels Polymorphie (oder Funktion als Parameter?)
5) Algorithmus zum Finden interessanter Wörter: könnte man auch für Verben haben wollen. Nomen sind halt leichter heuristisch einigermaßen erkennbar, daher sind es im ersten Wurf Nomen geworden.
6) Main: könnte man auch mit einem Einzeiler schreiben.
Zum debuggen finde ich es manchmal praktischer, wenn es nicht ganz so kompakt steht. Was meint ihr denn?
7) siehe TODOs im Code selbst.

Es gibt noch einen Haufen Aspekte, die man verbessern könnte. Ich poste den Code hier mal, damit Ihr einen Eindruck bekommt, was ich so mache.
Ich freue mich auf Euer Feedback!

LG
@Buchfink,
also dem meisten, was du selbst schon angemerkt hast, würde ich zustimmen.
Unter Punkt 2 würde ich aber nicht auf eine Klasse als Datenbehälter wechseln, solange ein Dictionary ausreicht. man kann ja auch die Struktur anpassen:
Beim iterieren über dict.items() würde ich nicht "key", "value" sondern aussagekräftige Namen verwenden.
Wenn eine Variable eine Datensequenz enthält würde ich den Plural verwenden:
"is not None" kann man ersetzen:
So etwas schreit nach einer List-Comprehension, Dict-Comprehension, oder Generator-Expression.
Aber die Funktion investigate_sayings() muss ja sowieso aufgeteilt werden, da kann man die Teile dann vielleicht schon als Generator anlegen.
also dem meisten, was du selbst schon angemerkt hast, würde ich zustimmen.
Unter Punkt 2 würde ich aber nicht auf eine Klasse als Datenbehälter wechseln, solange ein Dictionary ausreicht. man kann ja auch die Struktur anpassen:
Code: Alles auswählen
investigated_sayings = [
{
"originalsentence": "...",
"keywords": [...]
},
...
]
Wenn eine Variable eine Datensequenz enthält würde ich den Plural verwenden:
Code: Alles auswählen
for word in keywords:
Code: Alles auswählen
if substituted_word is not None:
Code: Alles auswählen
if substituted_word:
Code: Alles auswählen
interesting_words = []
for word in words:
if word.istitle():
interesting_words.append(word)
Zuletzt geändert von rogerb am Montag 1. November 2021, 11:57, insgesamt 1-mal geändert.
Zu deinen Punkten 3 und 4: Statt mit harded-coded Zuordnungen zu arbeiten, könntest du natürlich auch Wörterbücher in Dateiform hinterlegen. Aus meiner Sicht ist das für ein Hobby-Projekt um Python zu lernen vermutlich der Ansatz, mit dem du am schnellsten schöne Ergebnisse erhalten wirst. Was auch ganz gut und relativ einfach klappen könnte, ist statt "Voll-Reimen" Assonanzen und/oder Alliterationen/Stabreime zu nehmen. Oder man versucht es ggf. mit anderen heuristischen Ansätzen wie Ähnlichkeitsmaßen (Kölner Phonetik; (gewichtete) Levenshteindistanz, etc.). Wenn man das Erkennen von Endreimen automatisieren und das Problem "NLP-mäßig" angehen will, ist das nämlich nicht trivial und erfordert (denke ich) die Auseinandersetzung mit entsprechenden Algorithmen (z. B. zur Silbentrennung).
@rogerb und @nezzcarth
ganz lieben Dank für Euren Input.
Unter Delphi hätte ich vielleicht eine Klasse gebaut, die eine Generics-Liste aggregiert. Aber das fühlte sich _hier_ auch nicht richtig an, weil zu schwergewichtig. (Manchmal fühlen sich Dinge irgendwie nicht "pythonisch" an... )
Es ist immer wieder erstaunlich, welche Detailfülle sich da manchmal ergibt.
Aber es macht Spaß von Euch was zu lernen.
Ob ich allerdings intellektuell in der Lage bin, solche Algorithmen dann tatsächlich zu verstehen, steht auf einem anderen Blatt.
Ich bin da selbst gespannt wie ein Regenschirm
LG
ganz lieben Dank für Euren Input.
ja - genau sowas hab ich gesucht. Ich wusste nicht (wie/dass) das geht. Danke für den Hinweis!Unter Punkt 2 würde ich aber nicht auf eine Klasse als Datenbehälter wechseln, solange ein Dictionary ausreicht
Unter Delphi hätte ich vielleicht eine Klasse gebaut, die eine Generics-Liste aggregiert. Aber das fühlte sich _hier_ auch nicht richtig an, weil zu schwergewichtig. (Manchmal fühlen sich Dinge irgendwie nicht "pythonisch" an... )
--> das hab ich direkt korrigiert."is not None" kann man ersetzen:
Das werde ich erst googeln müssen, bevor ich konkrete Fragen dazu stellen kann.List-Comprehension, Dict-Comprehension, oder Generator-Expression.
Es ist immer wieder erstaunlich, welche Detailfülle sich da manchmal ergibt.

Es ist schwer zu sagen, wo das irgendwann hingeht. Ich finde Sprache als solches tatsächlich ziemlich interessant. (Mein Name ist eine Anspielung auf den Umstand, dass ich andauernd irgendein Buch lese).Wenn man das Erkennen von Endreimen automatisieren und das Problem "NLP-mäßig" angehen will, ist das nämlich nicht trivial und erfordert (denke ich) die Auseinandersetzung mit entsprechenden Algorithmen (z. B. zur Silbentrennung).
Ob ich allerdings intellektuell in der Lage bin, solche Algorithmen dann tatsächlich zu verstehen, steht auf einem anderen Blatt.

Ich bin da selbst gespannt wie ein Regenschirm

LG
@Buchfink: Ich würde an der Stelle keine Liste von Dicts mit implizit festgelegter Struktur benutzen, sondern eher ein `collections.namedtuple` oder auch ein `collections.defaultdict(list)`:
Wobei ich eher zum `defaultdict` tendieren würde, weil man darin direkt die Zuordnung eingebaut hat und nicht linear suchen muss, wenn man auf ein bestimmtes Sprichwort zugreifen will. Ein Dict mit beliebigen Strings als Keys ist IMHO nicht komisch.
Ich schreibe zwar auch gerne `if xs` oder `if not xs`, um auf eine leere Collection zu testen, aber `if foo` statt `if foo is not None` schreibe ich nie, weil das auch bei „leeren“ (bzw. allgemein „falsy“) Werten ins `if` springt. „Explicit is better than implicit.“
Code: Alles auswählen
In [1]: from collections import namedtuple, defaultdict
In [2]: Saying = namedtuple("Saying", "sentence, keywords")
In [4]: Saying("foo", ["bar"])
Out[4]: Saying(sentence='foo', keywords=['bar'])
# oder
In [5]: sayings = defaultdict(list)
In [6]: sayings["foo"].append("bar")
In [7]: sayings["foo"].append("baz")
In [9]: sayings["spam"].append("eggs")
In [10]: sayings
Out[10]: defaultdict(list, {'foo': ['bar', 'baz'], 'spam': ['eggs']})
Ich schreibe zwar auch gerne `if xs` oder `if not xs`, um auf eine leere Collection zu testen, aber `if foo` statt `if foo is not None` schreibe ich nie, weil das auch bei „leeren“ (bzw. allgemein „falsy“) Werten ins `if` springt. „Explicit is better than implicit.“
@narpfel,
Nicht sicher was du genau mit "leeren Werten" meinst, bei keinem dieser falsy Werte würde der if-Zweig ausgeführt.aber `if foo` statt `if foo is not None` schreibe ich nie, weil das auch bei „leeren“ (bzw. allgemein „falsy“) Werten ins `if` springt.
Code: Alles auswählen
falsy_values = [list(), dict(), tuple(), range(0), 0.0, 0, False, set(), None, ""]
for thing in falsy_values:
if thing:
print(f"{thing} is truthy")
else:
print(f"{thing} is falsy")
"""
[] is falsy
{} is falsy
() is falsy
range(0, 0) is falsy
0.0 is falsy
0 is falsy
False is falsy
set() is falsy
None is falsy
is falsy
"""
- __blackjack__
- User
- Beiträge: 14028
- Registriert: Samstag 2. Juni 2018, 10:21
- Wohnort: 127.0.0.1
- Kontaktdaten:
@rogerb: Genau das war gemeint: das trifft auf viele Werte zu, nicht nur auf `None`. Und vielleicht möchte man das nicht. Oft denkt man an solche Randfälle ja auch nicht. Oder denkt sich, der Fall kann ja eh nicht vorkommen. Und dann ist man stundenlang mit Fehlersuche beschäftigt. 

„A life is like a garden. Perfect moments can be had, but not preserved, except in memory. LLAP” — Leonard Nimoy's last tweet.
@narpfel
dankeschön für die Links und die Tipps
ich vermute, Du meinst _so_?
Beim Zerlegen der Funktion muss ich auch nochmal genau anschauen, was es mit "with open" _genau_ auf sich hat. Es sorgt wohl dafür (so habe ich die Tutorials verstanden), dass die Datei am Ende wieder geschlossen wird.
Zu
Oh je
Ich weiß soviel _nicht_
dankeschön für die Links und die Tipps
ich vermute, Du meinst _so_?
Code: Alles auswählen
from pathlib import Path
from collections import defaultdict
def investigate_sayings():
# TODO
# Verbesserung mittels wiki-api - Texte direkt aus Wikipedia "Sprichwörter" auslesen?
# Tutorial: https://wdqs-tutorial.toolforge.org/index.php/simple-queries/the-simplest-query/basic-sparql-query/
# Methode "tut" zu viel - Zerlegen in Beschaffung der Daten und Erstellen des Datenmodells
filename = Path("C:/Users/Carina/Documents/Projkete/WortspielGenerator/Sprueche.txt")
with open(filename, "r", encoding="UTF-8") as file:
investigated_sayings = defaultdict(list)
for line in file:
splitted = line.strip().split(".\"")
saying = splitted[0].strip().replace('\"', '')
if len(saying) == 1:
continue
words = saying.split(" ")
for word in words:
if word.istitle():
investigated_sayings[saying].append(word)
return investigated_sayings
Es kann durchaus sein, dass mein "ungutes Gefühl" daher rührt, dass Umlaute in Delphi ein Problem waren, bevor dort Unicode eingeführt wurde. Wenn das in Python kein Problem ist, dann passt das jaEin Dict mit beliebigen Strings als Keys ist IMHO nicht komisch.

Zu
Ich fürchte ich muss hier noch "falsy" googeln, denn den Begriff höre/lese ich heute zum ersten Mal.aber `if foo` statt `if foo is not None` schreibe ich nie, weil das auch bei „leeren“ (bzw. allgemein „falsy“) Werten ins `if` springt
Oh je

@narpfel
vielleicht liegt's auch daran, dass ich falsy noch nicht kenne.
`if foo is not None`. --> hört sich für meine Ohren "explizit" an. Und wenn explizit besser ist als implizit, wäre das nach meiner laienhaften Auffassung besser. Aber andererseits klingt es so als wäre diese Schreibweise extrem unüblich.
Sorry, dass ich jetzt hier nochmal nachhaken muss. Ich stehe wohl etwas auf der Leitung.
hm... jetzt bin ich etwas verwirrt.@rogerb: Flüchtigkeitsfehler, muss natürlich „nicht ins `if` springt“ heißen. Der Punkt ist, dass mit `if foo` nicht zwischen `None` und allgemein falsy unterscheiden werden kann, deswegen besser `if foo is not None`.
vielleicht liegt's auch daran, dass ich falsy noch nicht kenne.
`if foo is not None`. --> hört sich für meine Ohren "explizit" an. Und wenn explizit besser ist als implizit, wäre das nach meiner laienhaften Auffassung besser. Aber andererseits klingt es so als wäre diese Schreibweise extrem unüblich.
Sorry, dass ich jetzt hier nochmal nachhaken muss. Ich stehe wohl etwas auf der Leitung.
falsy sind Dinge, die als falsch gewertet werden. Auch wenn sie nicht der False-wert selbst sind. Zb 0, 0.0, “”, {}, [], () sind alle falsch. Man kann das auch selbst überladen auf eigenen Klassen, mit __nonzero__.
@__deets__
vielen lieben Dank für die Erklärung.
Ich hab das hier gefunden (https://developer.mozilla.org/de/docs/Glossary/Falsy) Der Link bezieht sich aber auf JavaScript. Ich hatte auch noch den Beispiel-Code von rogerb ausprobiert. Damit konnte ich es nachvollziehen.
Ich kenne das von Datenbanken so, dass man da aufpassen muss hinsichtlich "Null-Handling".
An "" (leer) hatte ich ehrlich gesagt gar nicht gedacht. Danke für den Hinweis.
Eigentlich möchte ich, dass er nur was in die Liste der substituierten Sprichwörter schreibt, wenn ein Reim gefunden wurde. Wenn aus irgendwelchen Gründen '"" (leer) zurückkäme (oder eben None) soll er nichts tun.
Insofern wäre "if substituted_word:" wohl semantisch besser. Sehe ich das richtig?
Ganz generell muss man meinem obigem Code wohl zum Vorwurf machen, dass er in Punkto Fehlerhandling fragil ist.
LG
vielen lieben Dank für die Erklärung.
Ich hab das hier gefunden (https://developer.mozilla.org/de/docs/Glossary/Falsy) Der Link bezieht sich aber auf JavaScript. Ich hatte auch noch den Beispiel-Code von rogerb ausprobiert. Damit konnte ich es nachvollziehen.
Ich kenne das von Datenbanken so, dass man da aufpassen muss hinsichtlich "Null-Handling".
An "" (leer) hatte ich ehrlich gesagt gar nicht gedacht. Danke für den Hinweis.
Eigentlich möchte ich, dass er nur was in die Liste der substituierten Sprichwörter schreibt, wenn ein Reim gefunden wurde. Wenn aus irgendwelchen Gründen '"" (leer) zurückkäme (oder eben None) soll er nichts tun.
Insofern wäre "if substituted_word:" wohl semantisch besser. Sehe ich das richtig?
Ganz generell muss man meinem obigem Code wohl zum Vorwurf machen, dass er in Punkto Fehlerhandling fragil ist.
LG
- __blackjack__
- User
- Beiträge: 14028
- Registriert: Samstag 2. Juni 2018, 10:21
- Wohnort: 127.0.0.1
- Kontaktdaten:
Aus dem ``split(" ")`` würde ich ein ``split()`` machen. Sonst kann es die leere Zeichenkette tatsächlich geben:
Das wird durch `istitle()` zwar rausgefiltert, aber bei so Freitext würde ich eher auf Nummer sicher gehen.
Das Aufteilen an '."' finde ich auch ein bisschen gewagt. Keine Sprüche die mit einem anderen Satzzeichen enden können? Rethorische Fragen? Ausrufe? Und wenn die '."'-Kombination später noch mal kommt, ist's auch komisch mit `split()`. Ich würde da eher `partition()` verwenden. Dann kann man auch einfach testen ob der Trenner überhaupt vorkam. Falls der nämlich nicht vorkommt, nimmt der Code einfach die komplette Zeile.
Warum ersetzen von '"' in der ganzen Zeile und nicht nur am Anfang? Den Test auf `len()` und 1 mag ich auch nicht so besonders. Das ist irgendwie so "indirekt" das da die Zeilen mit den einzelnen Buchstaben erwischt werden.
Ich hätte es wahrscheinlich in diese Richtung geschrieben:
Wobei ich kein Fan von Fehlermeldungen und weitermachen bin, ich hätte das wahrscheinlich als Ausnahmen gemacht und abgebrochen wenn da was kommt was nicht ins erwartete Schema passt.
Und `print()` wäre eher Logging, wenn es keine Ausnahmen sein sollen. Standardbibliothek oder `loguru`.
Code: Alles auswählen
In [418]: "foo bar".split(" ")
Out[418]: ['foo', '', 'bar']
Das Aufteilen an '."' finde ich auch ein bisschen gewagt. Keine Sprüche die mit einem anderen Satzzeichen enden können? Rethorische Fragen? Ausrufe? Und wenn die '."'-Kombination später noch mal kommt, ist's auch komisch mit `split()`. Ich würde da eher `partition()` verwenden. Dann kann man auch einfach testen ob der Trenner überhaupt vorkam. Falls der nämlich nicht vorkommt, nimmt der Code einfach die komplette Zeile.
Warum ersetzen von '"' in der ganzen Zeile und nicht nur am Anfang? Den Test auf `len()` und 1 mag ich auch nicht so besonders. Das ist irgendwie so "indirekt" das da die Zeilen mit den einzelnen Buchstaben erwischt werden.
Ich hätte es wahrscheinlich in diese Richtung geschrieben:
Code: Alles auswählen
def investigate_sayings():
# TODO
# Verbesserung mittels wiki-api - Texte direkt aus Wikipedia "Sprichwörter" auslesen?
# Tutorial: https://wdqs-tutorial.toolforge.org/index.php/simple-queries/the-simplest-query/basic-sparql-query/
# Methode "tut" zu viel - Zerlegen in Beschaffung der Daten und Erstellen des Datenmodells
filename = Path(
"C:/Users/Carina/Documents/Projkete/WortspielGenerator/Sprueche.txt"
)
with open(filename, "r", encoding="UTF-8") as file:
investigated_sayings = defaultdict(list)
for line_number, line in enumerate(file, 1):
if line.startswith('"'):
saying, seperator, _ = line[1:].partition('."')
if seperator:
for word in saying.split():
if word.istitle():
investigated_sayings[saying].append(word)
else:
print(
f"Fehler: Ende von Spruch in Zeile {line_number} nicht gefunden."
)
else:
line = line.rstrip()
if not (len(line) == 1 and line.isalpha()):
print(
f"Fehler: Unerwartete(s) Zeichen in Zeile {line_number}."
)
return investigated_sayings
Und `print()` wäre eher Logging, wenn es keine Ausnahmen sein sollen. Standardbibliothek oder `loguru`.
„A life is like a garden. Perfect moments can be had, but not preserved, except in memory. LLAP” — Leonard Nimoy's last tweet.
@__blackjack__
oh woow, dankeschön!
). Bzw. mir sind auch vermutlich die Fallstricke der einzelnen Konstellationen noch nicht immer ganz klar. Oder mit anderen Worten: mir mangelt es noch an Erfahrung im Hinblick auf Python.
Wobei ich ehrlich gesagt nicht weiß, ob "print(..)" im Backend in Python akzeptiert/üblich ist.
Im konkreten Fall ist das zum Verständnis aber vermutlich ok, nehme ich an.
*) irgendjemand sagte mir mal, dass der Begriff Backend mit Vorsicht zu genießen sei. Ich habe aber leider die Begründung vergessen.
Aktuell versuche ich noch den zweiten Vorschlag von rogerb einzubauen. Da "kämpfe" ich noch mit den Tutorials, aber ich glaube, dass ich jetzt weiß, was er meinte.
LG
oh woow, dankeschön!
Im Hinblick auf das Fehlermanagement habe ich leider noch nicht geschafft, die ganzen Randfälle zu testen (Und ja, selbstredend gehört sowas dazuAus dem ``split(" ")`` würde ich ein ``split()`` machen. Sonst kann es die leere Zeichenkette tatsächlich geben:

ja ich tendiere auch eher zu Exceptions, da es sich bei dieser Funktion um "Backend"*-Logik handelt.Wobei ich kein Fan von Fehlermeldungen und weitermachen bin, ich hätte das wahrscheinlich als Ausnahmen gemacht und abgebrochen wenn da was kommt was nicht ins erwartete Schema passt.
Wobei ich ehrlich gesagt nicht weiß, ob "print(..)" im Backend in Python akzeptiert/üblich ist.
Im konkreten Fall ist das zum Verständnis aber vermutlich ok, nehme ich an.
*) irgendjemand sagte mir mal, dass der Begriff Backend mit Vorsicht zu genießen sei. Ich habe aber leider die Begründung vergessen.
Aktuell versuche ich noch den zweiten Vorschlag von rogerb einzubauen. Da "kämpfe" ich noch mit den Tutorials, aber ich glaube, dass ich jetzt weiß, was er meinte.
LG
@Buchfink,
Beim zweiten Hinschauen, würde ich vielleicht ganz auf das Erstellen dieser Datenstruktur verzichten. Ich will dich aber nicht davon abhalten es trotzdem mal zu versuchen.
"investigated_sayings" enthält keine zusätzliche Informationen. Du verbindest lediglich Daten, die zusammengehören in einer neuen Datenstruktur. Das kann ineffizient werden.
Ausschlaggebend ob ein Wort zu den "interesting_words" gehört hängt davon ab ob es im Dictionary der Reimwörter vorhanden ist. Mit istitle() bekommst du auch viele Wörter, für die es später gar kein Reimwort gibt.
Daher wäre es besser nicht istitle() abzufragen, sondern "ist in Reimwörter". Dann gibt "get_rhyme()" auch immer etwas Verwertbares zurück. Jetzt wird ja des Öfteren None zurückgegeben, was dann wieder die Prüfung gegen None überhaupt erst nötig macht.
Man kann auch beim Iterieren über die Sprichwörter direkt die Umwandlung vornehmen. Dadurch dürfte eine Iteration über den gesamten Datensatz wegfallen.
Da ich jetzt bei __blackjack__ auch das "is not None" sehe, noch ein Kommentar dazu: Natürlich würde jeder erstmal dem Satz "explicit is better than implicit" zustimmen. Aber hier sehe ich es etwas anders. None enthält den Informationswert: "Kein Reimwort gefunden". Das muss aber nicht zwingend an None gebunden sein. Dafür kommt jeder falsy Wert in Frage. Hier ist es nur zufällig None, da das der default Wert für dict.get() ist. Würde man sich später für eine andere Vorgehensweise entscheiden, müsste man von "if is not None" zu "if is not other_falsy_value" ändern. Durch die dynamische Typisierung von Python hat man hier den Vorteil, dass man sich gerade nicht festlegen muss und statt dessen auf alle falsy-Werte eine Antwort hat. Aber das muss man im Einzelfall abwägen und ich hatte ja schon gesagt, dass man diese Abfrage komplett auslassen kann.
Code: Alles auswählen
Aktuell versuche ich noch den zweiten Vorschlag von rogerb einzubauen. Da "kämpfe" ich noch mit den Tutorials, aber ich glaube, dass ich jetzt weiß, was er meinte.
"investigated_sayings" enthält keine zusätzliche Informationen. Du verbindest lediglich Daten, die zusammengehören in einer neuen Datenstruktur. Das kann ineffizient werden.
Ausschlaggebend ob ein Wort zu den "interesting_words" gehört hängt davon ab ob es im Dictionary der Reimwörter vorhanden ist. Mit istitle() bekommst du auch viele Wörter, für die es später gar kein Reimwort gibt.
Daher wäre es besser nicht istitle() abzufragen, sondern "ist in Reimwörter". Dann gibt "get_rhyme()" auch immer etwas Verwertbares zurück. Jetzt wird ja des Öfteren None zurückgegeben, was dann wieder die Prüfung gegen None überhaupt erst nötig macht.
Man kann auch beim Iterieren über die Sprichwörter direkt die Umwandlung vornehmen. Dadurch dürfte eine Iteration über den gesamten Datensatz wegfallen.
Da ich jetzt bei __blackjack__ auch das "is not None" sehe, noch ein Kommentar dazu: Natürlich würde jeder erstmal dem Satz "explicit is better than implicit" zustimmen. Aber hier sehe ich es etwas anders. None enthält den Informationswert: "Kein Reimwort gefunden". Das muss aber nicht zwingend an None gebunden sein. Dafür kommt jeder falsy Wert in Frage. Hier ist es nur zufällig None, da das der default Wert für dict.get() ist. Würde man sich später für eine andere Vorgehensweise entscheiden, müsste man von "if is not None" zu "if is not other_falsy_value" ändern. Durch die dynamische Typisierung von Python hat man hier den Vorteil, dass man sich gerade nicht festlegen muss und statt dessen auf alle falsy-Werte eine Antwort hat. Aber das muss man im Einzelfall abwägen und ich hatte ja schon gesagt, dass man diese Abfrage komplett auslassen kann.