Tutorial: Einfache Textmenüs mit Python

Gute Links und Tutorials könnt ihr hier posten.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

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 :P

Es sind sicherlich noch einige Fehler darin enthalten, daher würde ich mich über "Debug"-Hilfe freuen :-)
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Leonidas
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.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
snafu
User
Beiträge: 6741
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Leonidas hat geschrieben:Operatoren sind keine Objekte (``+.quak()``)
Hehe, das wär's noch:

Code: Alles auswählen

+(1, 1)
8)

(ist natürlich nicht ernsthaft umsetzbar, weil dann einiges an Missinterpretationen bezüglich geklammerter Ausdrücke vorprogrammiert wäre...)
webspider
User
Beiträge: 485
Registriert: Sonntag 19. Juni 2011, 13:41

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 :)
frabron
User
Beiträge: 306
Registriert: Dienstag 31. März 2009, 14:36

Nett 8)

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 :D. 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'
Benutzeravatar
snafu
User
Beiträge: 6741
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

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*
Benutzeravatar
snafu
User
Beiträge: 6741
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

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'
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.
Benutzeravatar
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 :mrgreen:

@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
Benutzeravatar
snafu
User
Beiträge: 6741
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Hyperion hat geschrieben:@Thema "Dictionary": [...] verliert man ja die Reihenfolge der Einträge. Das ist bei einem Menü sicherlich wenig wünschenswert
Ok, sehr gutes Gegenargument. Hatte ich nicht bedacht.

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.
BlackJack

@snafu: ``+(1, 1)`` ist zumindest syntaktisch korrekt. Nur das Tupel halt nichts mit einem unären ``+``-Operator anfangen können. :-)
Leonidas
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)
8)

Code: Alles auswählen

(+, 1, 1)
und dann noch die Kommas weglassen

Code: Alles auswählen

(+ 1 1)
Oha 8)
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
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
Benutzeravatar
snafu
User
Beiträge: 6741
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

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. ;)
Zuletzt geändert von snafu am Freitag 20. April 2012, 13:19, insgesamt 2-mal geändert.
Benutzeravatar
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...
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Benutzeravatar
snafu
User
Beiträge: 6741
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Hyperion hat geschrieben:Hast Du Dir `classymenu.py` mal angeguckt? ;-)
Nee, hab ich nicht. :oops:

Ich meinte das aber wie gesagt auch als Tutorial (haste ja schon bemerkt).
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

snafu hat geschrieben: Nee, hab ich nicht. :oops:
Da muss ich immer hieran denken: Link :mrgreen:
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Benutzeravatar
/me
User
Beiträge: 3556
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

Hyperion hat geschrieben:Es sind sicherlich noch einige Fehler darin enthalten, daher würde ich mich über "Debug"-Hilfe freuen :-)
Das Ding ist gut. Richtig gut.

Nur gegenüber

Code: Alles auswählen

if choice in range(len(menu)):
würde ich

Code: Alles auswählen

if 0 <= choice < len(menu):
bevorzugen.

*edit* Ich hatte vorher einen Fehler gesehen wo keiner war.
Benutzeravatar
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
frabron
User
Beiträge: 306
Registriert: Dienstag 31. März 2009, 14:36

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.
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?

Code: Alles auswählen

# Bitte so nicht machen, denn das ist ein typischer Anfängerfehler. Wieso steht weiter unten
Und die Erklärung noch um die Übeltäter ergänzen:
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.
So kann man auch als Laie das Anti-Pattern erkennen, da es nun konkret benannt ist durch Nennung von `range()` und `len()`.

Frank
Benutzeravatar
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 :-D
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Antworten