Hallo zusammen,
da ich schon häufiger in meinen Beiträgen ähnlich zu diesem hier dazu animiert fühlte, Wege aufzuzeigen, wie man Menüs generisch designen kann, habe ich mir mal die Mühe gemacht, ein Tutorial sowie diverse kleinere Module zu diesem Thema zu verfassen.
Das ganze richtet sich in erster Linie an Anfänger, die noch wenig vertraut in der Anwendung von Datenstrukturen sind. Ich hoffe, dass man damit viele User erreicht - zumal ich es extra auf Deutsch verfasst habe
Es sind sicherlich noch einige Fehler darin enthalten, daher würde ich mich über "Debug"-Hilfe freuen
Tutorial: Einfache Textmenüs mit Python
- Hyperion
- Moderator
- Beiträge: 7478
- Registriert: Freitag 4. August 2006, 14:56
- Wohnort: Hamburg
- Kontaktdaten:
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
assert encoding_kapiert
-
- Python-Forum Veteran
- Beiträge: 16025
- Registriert: Freitag 20. Juni 2003, 16:30
- Kontaktdaten:
Oh, das ist ja schick, gefällt mir. Auch vom Schreibstil find ich das entertaining
Aber kurzer Hint: "In Python ist alles ein Objekt" ist ewas ungenau. In Python ist alles ein objekt, was man an einen Namen binden kann. Operatoren sind keine Objekte (``+.quak()``) und Syntax ist auch kein Objekt (also etwa kein ``if``-Objekt) obwohl man, wenn man nun noch extremer pedantisch sein will sich zu sowohl Operatoren (Modul ``operator``) als auch Syntax (``ast``) sich entsprechende Objekte holen kann.
Aber kurzer Hint: "In Python ist alles ein Objekt" ist ewas ungenau. In Python ist alles ein objekt, was man an einen Namen binden kann. Operatoren sind keine Objekte (``+.quak()``) und Syntax ist auch kein Objekt (also etwa kein ``if``-Objekt) obwohl man, wenn man nun noch extremer pedantisch sein will sich zu sowohl Operatoren (Modul ``operator``) als auch Syntax (``ast``) sich entsprechende Objekte holen kann.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Hehe, das wär's noch:Leonidas hat geschrieben:Operatoren sind keine Objekte (``+.quak()``)
Code: Alles auswählen
+(1, 1)
(ist natürlich nicht ernsthaft umsetzbar, weil dann einiges an Missinterpretationen bezüglich geklammerter Ausdrücke vorprogrammiert wäre...)
Ich arbeite gerade die fortgeschritteneren Code-Beispiele durch. Interessant zu sehen wie man das einfache Beispiel noch weiter abstrahieren kann. Sobald die Rechtschreibfehler ausgetilgt sind ("breath-search"), könnte man es fast schon in den Wiki packen (oder zumindest darauf verlinken) und dann hätten wir endlich das immer wieder verlangte Dispatching-Tutorial
Nett
Beim Durchlesen ist mir mal nix gravierendes Aufgefallen. Ausser vielleicht die Stelle mit dem AntiPattern, da könnte man evt. noch darauf eingehen, warum man `range(len())` nicht benutzen sollte ... das steht imo etwas unbegründet im Raum - und man könnte es ja noch in Rot und Fett schreiben . Ansonsten kam mir beim Lesen die Idee, dass ein dict() auch als Datenstruktur für das Menü geeignet sein könnte anstelle der verschachtelten Liste.
Beim Durchlesen ist mir mal nix gravierendes Aufgefallen. Ausser vielleicht die Stelle mit dem AntiPattern, da könnte man evt. noch darauf eingehen, warum man `range(len())` nicht benutzen sollte ... das steht imo etwas unbegründet im Raum - und man könnte es ja noch in Rot und Fett schreiben . Ansonsten kam mir beim Lesen die Idee, dass ein dict() auch als Datenstruktur für das Menü geeignet sein könnte anstelle der verschachtelten Liste.
Code: Alles auswählen
>>> d = {'a':1, 'b': 2}
>>> d.keys()[0]
'a'
Zum Thema: Ich finde es super, dass zuerst auf ein Fallbeispiel/typische Problematik eingangen wird und darauf aufbauend die weiteren Erklärungen geschrieben sind. Das dürfte mehr Interesse wecken, als wenn man zuerst mit dem theoretischen Bla-Bla angefangen hätte. *thumbs-up*
Finde ich auch. Und wenn man bei einer Liste bleiben möchte, dann sollten IMHO zumindest die "Menüpunkt <=> Funktion"-Pärchen durch Tupel repräsentiert werden.frabron hat geschrieben:Ansonsten kam mir beim Lesen die Idee, dass ein dict() auch als Datenstruktur für das Menü geeignet sein könnte anstelle der verschachtelten Liste.Code: Alles auswählen
>>> d = {'a':1, 'b': 2} >>> d.keys()[0] 'a'
- Hyperion
- Moderator
- Beiträge: 7478
- Registriert: Freitag 4. August 2006, 14:56
- Wohnort: Hamburg
- Kontaktdaten:
Danke für die Kritik
@Leonidas: Stimmt. Wenn man schon "genau" sein will, dann sollte man es auch sein. Das werde ich ändern.
@webspider: Oops, ja wurde spät neulich und gestern... wobei so ne Atem frische Suche doch auch was hat
@frabron: Wie würdest Du denn begründen, dass man diesen Anti-Pattern nicht verwenden sollte? Ich dachte die nachfolgende Darstellung würde ausreichen. Aber vielleicht muss da tatsächlich mehr "Kausalität" rein.
@Thema "Dictionary": Wie meint ihr das genau? Wollt ihr ein Dict für die "Menütext" <-> "Funktions"-Pärchen? Oder für die äußere Liste? Ersteres erschien mir aus Tippgründen wenig angenehm und bei zwei Einträgen verliert man die Zuordnung ja nicht aus dem Auge. Bei letzterem verliert man ja die Reihenfolge der Einträge. Das ist bei einem Menü sicherlich wenig wünschenswert. Und auf Geschichten wir dem `OrderedDict` wollte ich nicht aufbauen, da ich wirklich nur grundlegende Strukturen verwenden will.
So ein Dispatcher mittels Dictionary macht imho wirklich nur dann Sinn, wenn man über einen String ein Verhalten steuern will. Z.B. wie ich das Nummerierungsschema in meinem simplequiz (ganz unten, Zeilen 243 u. 254) auswählbar mache.
Oder habe ich Eure Einwände hier falsch interpretiert?
Man könnte natürlich noch auf Tupel eingehen und dem Leser vermitteln, dass das vom Verhalten an sich mit dem einer Liste übereinstimmt, außer beim Thema Änderungen. In meinen Modulen verwende ich ja schließlich auch Tupel
@Leonidas: Stimmt. Wenn man schon "genau" sein will, dann sollte man es auch sein. Das werde ich ändern.
@webspider: Oops, ja wurde spät neulich und gestern... wobei so ne Atem frische Suche doch auch was hat
@frabron: Wie würdest Du denn begründen, dass man diesen Anti-Pattern nicht verwenden sollte? Ich dachte die nachfolgende Darstellung würde ausreichen. Aber vielleicht muss da tatsächlich mehr "Kausalität" rein.
@Thema "Dictionary": Wie meint ihr das genau? Wollt ihr ein Dict für die "Menütext" <-> "Funktions"-Pärchen? Oder für die äußere Liste? Ersteres erschien mir aus Tippgründen wenig angenehm und bei zwei Einträgen verliert man die Zuordnung ja nicht aus dem Auge. Bei letzterem verliert man ja die Reihenfolge der Einträge. Das ist bei einem Menü sicherlich wenig wünschenswert. Und auf Geschichten wir dem `OrderedDict` wollte ich nicht aufbauen, da ich wirklich nur grundlegende Strukturen verwenden will.
So ein Dispatcher mittels Dictionary macht imho wirklich nur dann Sinn, wenn man über einen String ein Verhalten steuern will. Z.B. wie ich das Nummerierungsschema in meinem simplequiz (ganz unten, Zeilen 243 u. 254) auswählbar mache.
Oder habe ich Eure Einwände hier falsch interpretiert?
Man könnte natürlich noch auf Tupel eingehen und dem Leser vermitteln, dass das vom Verhalten an sich mit dem einer Liste übereinstimmt, außer beim Thema Änderungen. In meinen Modulen verwende ich ja schließlich auch Tupel
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
assert encoding_kapiert
Ok, sehr gutes Gegenargument. Hatte ich nicht bedacht.Hyperion hat geschrieben:@Thema "Dictionary": [...] verliert man ja die Reihenfolge der Einträge. Das ist bei einem Menü sicherlich wenig wünschenswert
Tupel nehme ich oft aus "ästhetischen" Gründen. Das ist dann primär weniger eine Sache der Funktionalität, sondern es hat eher semantische Gründe für mich. Ein Pärchen (mit naturgemäß feststehender Länge) ist halt etwas anderes als eine Liste, find ich.
@snafu: ``+(1, 1)`` ist zumindest syntaktisch korrekt. Nur das Tupel halt nichts mit einem unären ``+``-Operator anfangen können.
-
- Python-Forum Veteran
- Beiträge: 16025
- Registriert: Freitag 20. Juni 2003, 16:30
- Kontaktdaten:
snafu hat geschrieben:Hehe, das wär's noch:Code: Alles auswählen
+(1, 1)
Code: Alles auswählen
(+, 1, 1)
Code: Alles auswählen
(+ 1 1)
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
- Hyperion
- Moderator
- Beiträge: 7478
- Registriert: Freitag 4. August 2006, 14:56
- Wohnort: Hamburg
- Kontaktdaten:
Ich habe mal eine kleine Passage zu Tupeln eingebaut ("Auf der Zielgeraden"). Ich hoffe damit wird klar, worin die Unterschiede liegen.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
assert encoding_kapiert
Teil 2 des Tutorials könnte sich ja vielleicht damit beschäftigen, wie man das Menü als Klasse realisieren kann, damit man (beziehe mich hier auf `simplemenu.py`) das Menüobjekt nicht umherreichen muss und eine kleine Abstraktionsschicht hat. Ich denke da z.B. an Methoden wie `create_menu_item()` und sowas.
So ein Menü kann aber durchaus eine von `dict` abgeleitete Klasse sein, damit man die ganze Komfortfunktionalität, wie z.B. das Iterieren über die Menüpunkte frei Haus mit dabei hat.
Das wäre dann aber wohl eher was für Fortgeschrittene, die sich mit OOP befassen möchten...
//edit: Ups, ich wieder mit meinem `dict`. Bitte aus dem Protokoll streichen.
So ein Menü kann aber durchaus eine von `dict` abgeleitete Klasse sein, damit man die ganze Komfortfunktionalität, wie z.B. das Iterieren über die Menüpunkte frei Haus mit dabei hat.
Das wäre dann aber wohl eher was für Fortgeschrittene, die sich mit OOP befassen möchten...
//edit: Ups, ich wieder mit meinem `dict`. Bitte aus dem Protokoll streichen.
Zuletzt geändert von snafu am Freitag 20. April 2012, 13:19, insgesamt 2-mal geändert.
- Hyperion
- Moderator
- Beiträge: 7478
- Registriert: Freitag 4. August 2006, 14:56
- Wohnort: Hamburg
- Kontaktdaten:
Hast Du Dir `classymenu.py` mal angeguckt?
Aber ok, man könnte natürlich darüber auch noch etwas schreiben...
Aber ok, man könnte natürlich darüber auch noch etwas schreiben...
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
assert encoding_kapiert
- Hyperion
- Moderator
- Beiträge: 7478
- Registriert: Freitag 4. August 2006, 14:56
- Wohnort: Hamburg
- Kontaktdaten:
Da muss ich immer hieran denken: Linksnafu hat geschrieben: Nee, hab ich nicht.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
assert encoding_kapiert
Das Ding ist gut. Richtig gut.Hyperion hat geschrieben:Es sind sicherlich noch einige Fehler darin enthalten, daher würde ich mich über "Debug"-Hilfe freuen
Nur gegenüber
Code: Alles auswählen
if choice in range(len(menu)):
Code: Alles auswählen
if 0 <= choice < len(menu):
*edit* Ich hatte vorher einen Fehler gesehen wo keiner war.
- Hyperion
- Moderator
- Beiträge: 7478
- Registriert: Freitag 4. August 2006, 14:56
- Wohnort: Hamburg
- Kontaktdaten:
@me: Danke. Ich habe die Überprüfung auf Deinen Vorschlag umgestellt.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
assert encoding_kapiert
Meiner Meinung nach kommt die Warnung einen Absatz zu spät. Ich finde, gerade wegen der Häufigkeit des Antreffens, sollte man das direkt als erstes unter den Code-Kasten schreiben. Oder als Kommentar über die Schleife?Hyperion hat geschrieben:Danke für die Kritik
@frabron: Wie würdest Du denn begründen, dass man diesen Anti-Pattern nicht verwenden sollte? Ich dachte die nachfolgende Darstellung würde ausreichen. Aber vielleicht muss da tatsächlich mehr "Kausalität" rein.
Code: Alles auswählen
# Bitte so nicht machen, denn das ist ein typischer Anfängerfehler. Wieso steht weiter unten
So kann man auch als Laie das Anti-Pattern erkennen, da es nun konkret benannt ist durch Nennung von `range()` und `len()`.Leider ist obiger Code schlecht! Er ist eines der berühmten Anti-Pattern, die man häufig bei Anfängern oder Umsteigern von anderen Sprachen sieht. Durch den Aufruf zweier, in diesem Zusammenhang, unnötiger Funktionen (range() und len()) wirkt der Code unleserlicher, ausserdem ist er unnötig kompliziert.
Frank
- Hyperion
- Moderator
- Beiträge: 7478
- Registriert: Freitag 4. August 2006, 14:56
- Wohnort: Hamburg
- Kontaktdaten:
Ich habe Deine Ideen zum Teil übernommen. Die Warnung im Code mag ich nicht einführen, da der Leser zum einen sonst frühzeitig abgelenkt wird und zum anderen demotiviert wird, das ganze zu durchdenken. Er soll ja genau darauf hingeführt werden, wie viel einfacher und leserlicher die Lösung mittels `enumerate` ist. Genauso so habe ich es ja auch mit der "schlechten" Lösung des Gesamtproblems gehandhabt: Erst einmal vorstellen und dann erklären, wieso man das nicht so machen sollte und die sinnvolle Lösung aufzeigen. Ich denke, dass ich so der Stil des Tutorials und den mag ich nicht brechen. Wer die Warnung dann überliest, der wird das Tutorial eh nicht richtig durcharbeiten und somit nichts lernen
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
assert encoding_kapiert