python2to3

Du hast eine Idee für ein Projekt?
Nobuddy
User
Beiträge: 1019
Registriert: Montag 30. Januar 2012, 16:38

Ok, denke daß ich es gelöst habe! :D
Habe wieder viel zu kompliziert gedacht, aber EyDu gab da den entsprechenden Hinweis.

Die Hauptfunktion mit dem Hauptmenü ist 'handle_menu(menu)'.
Aktuell bearbeitetes Menü ist '["Eintrag ändern", load]'.
Funktion 'load(counter, dataline)' wird gestartet und startet die Funktion 'search_entry()'. Hier gebe ich meinen Suchbegriff ein und erhalte entsprechend ein Ergebnis. Dieses Ergebnis wird über die Funktion 'pass_func(counter, data)' an die Funktion load(counter, dataline) weitergeleitet. Dort treffe ich dann die Auswahl bei mehreren Ergebnissen, die dann anschließend weiterverarbeitet wird.
Die Funktion 'load(counter, dataline)' ist noch nicht ganz fertig!

Habe es hier https://gist.github.com/3192265 mal rein gestellt.

PS: Nachtrag
Bei obigem Code, hatte ich den direkten Aufruf der Funktion 'search_entry()' nicht berücksichtigt.
Hier würde ich dann automatisch in der Funktion 'load(counter, dataline)' landen, was ja in dem Fall nicht gewünscht ist.
Ich habe den Code entsprechend geändert, siehe hier: https://gist.github.com/3192727
Benutzeravatar
snafu
User
Beiträge: 6862
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Du hast in einer Funktion immer noch `global` drin. Wenn du `global` vermeiden willst, aber trotzdem mehrere Funktionen das selbe Objekt nutzen sollen, dann muss entweder das betreffende Objekt als Argument hin und her gereicht werden und du schaust dir mal das Konzept von Klassen an. In Klassen wäre nämlich ein gemeinsamer Zugriff mithilfe sogenannter Instanzattribute möglich. Für den Anfang und angesichts deiner "Programmierkünste" (nichts für ungut) würde ich jedoch bis auf Weiteres die Methode des Herumreichens empfehlen.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Vielleicht noch mal ein Beispiel, damit es noch offensichtlicher wird:

Code: Alles auswählen

def small_function(param):
    # was wichtiges machen, dazu wird ein Objekt (`param`) benötigt,
    # welches übergeben werden muss

def another_function(data, obj):
    # diese Funktion braucht nur `data`, aber ruft `small_function` auf
    # also braucht sie das Objekt, auf dem `small_function` arbeitet, um
    # es an diese weiterreichen zu können!
    result = small_function(obj)
    # ...

def function():
    # hier wird das Objekt erstellt, welches einmal als `data`-Parameter
    # übergeben wird
    a = something()
    # und hier der spätere `obj`-Parameter
    b = somthing_else()
    another_function(a, b)

# irgend wo anders:
function()
Die Namen habe ich absichtlich unterschiedlich gewählt, damit klar wird, dass es nicht um die Namen in einer Funktion geht, sondern nur um die daran gebundenen Objekte.

Wie Du siehst habe ich hier kein `global` drin. Was ist daran nun schwierig?
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Hab grad mal in den Code geguckt - und hätte es lassen sollen :-D

Code: Alles auswählen

            choice = int(input("Nummer: ")) - 1
            if menu[choice][0] == 'Eintrag suchen':
                menu[choice][1]('')
            elif menu[choice][0] == 'Eintrag ändern':
                menu[choice][1]('', '')
            else:
                menu[choice][1]()
Das hier ist doch total unnötig im doppelten Sinne! Du willst ja anhand von `choice` unterschiedlich die in der Menü-Struktur verpackten Funktionen aufrufen. Die Vergleiche ließen sich also auf einen *direkten* Vergleich bezüglich `choice` reduzieren:

Code: Alles auswählen

if choice == 0:
    menu[choice][1]('')
elif choice == 1:
    menu[choice][1]('', '')
else:
    menu[choice][1]()
Wichtig ist Dir doch nicht, wie der Menütext an Position 0 lautet, sondern, dass Position 0 gewählt worden ist!

Aber auch das ist *schlecht*! Du konterkarierst damit den dynamischen Ansatz des Menüsystems... eigentlich kannst Du Dir das ganze System sparen, wenn Du dann doch wieder individuelle Abfragen zu einem Menüpunkt machst. In meinem Tutorial bin ich doch u.a. auch darauf eingegangen, dass das *Ändern* von Menüeinträgen zu Problemen führen kann. Genau diese Probleme handelst Du Dir hier wieder ein... überlege Dir mal, was passiert, wenn Du einen Menüeintrag vor alle anderen einfügst... genau damit startet iirc. mein Tutorial doch auch.

Du kannst dem aus dem Weg gehen, indem Du z.B. Deine Funktionen mit einer identischen Signatur versiehst und diese dann wieder allgemein aufrufen kannst. Wobei ich im Moment noch nicht sehe, was einem ein leerer String bringt. Das könnte man auch via `functools.partial` vorher erreichen, oder einfach Default-Parameter in der Funktion nutzen (`func(param='')`).

Wenn Du für jede Funktion unterschiedliche und vor allem dynamische Argumente brauchst, musst Du Dir etwas mehr überlegen, *wie* und *wo* diese Objekte erstellt werden und wie man die ggf. in ein Objekt zusammenfassen kann.

Vielleicht muss ich es noch mal anfassen und eine Fortsetzung schreiben, die so etwas mal vorstellt.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Nobuddy
User
Beiträge: 1019
Registriert: Montag 30. Januar 2012, 16:38

Hallo zusammen,
@snafu, danke für die Info, hatte es vergessen herauszunehmen. :wink:

@Hyperion, danke für Dein Beispiel, ist wirklich gut erklärt, so was ist nie verkehrt. :wink:
Habe absichtlich nicht die Nummer von 'choice' verwendet, da der Menünamen besser verständlich ist und sollte man mal 'menu' aus einem Grund die Reihenfolge anders gestalten, würde die Nummer von 'choice' nicht mehr stimmen.
vorher:

Code: Alles auswählen

menu = [
    ["Eintrag suchen", search_entry], # 0
    ["Eintrag hinzufügen", add_entry], # 1
    ["Eintrag ändern", load], # 2
    ["Eintrag löschen", remove_entry], # 3
    ["Eintrag speichern", save], # 4
    ["Beenden", quit] # 5
]
nachher:

Code: Alles auswählen

menu = [
    ["Eintrag hinzufügen", add_entry], # 0 war vorher 1
    ["Eintrag löschen", remove_entry], # 2 war vorher 3
    ["Eintrag ändern", load], # 3 war vorher 2
    ["Eintrag speichern", save], # 4
    ["Eintrag suchen", search_entry], # 4 war vorher 0
    ["Beenden", quit] # 5
]
Das war mein Ansatz.

Natürlich hast Du Recht, daß der dynamische Ansatz des Menüsystems, so über den Haufen geworfen wird.
Da ich das nicht will, bleibt nur eins ... die Menüeinträge so zu belassen, dann kann ich auch dies 'if choice == 0:' verwenden.

Ich werde mich auch Deinem Vorschlag anschließen, via `functools.partial` vorher, oder einfach Default-Parameter in der Funktion nutzen (`func(param='')`). Da werde ich mich mal durchlesen.

Auch über Deinen vorletzten Absatz, werde ich mir Gedanken machen, möchte aber zuerst das zuvor Besprochene umsetzen.

Auf eine Fortsetzung zu Deinem Textmenü-Tutorial, freue ich mich schon ... :wink:
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Nobuddy hat geschrieben: Natürlich hast Du Recht, daß der dynamische Ansatz des Menüsystems, so über den Haufen geworfen wird.
Da ich das nicht will, bleibt nur eins ... die Menüeinträge so zu belassen, dann kann ich auch dies 'if choice == 0:' verwenden.
Nö. Wenn Du wirklich dynamisch bleiben willst, darfst Du in der Logik der Menü-Funktion *keine* Annahmen über Einträge treffen. Also z.B. dass der 0. Eintrag so und so und mit dem und dem Parameter aufgerufen werden muss. Du kannst also entweder auf diese Logik verzichten, z.B indem Du eine fixe Anzahl an Parametern übergibst, oder diese Logik irgend wie in die Menüdefinition einbauen.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Nobuddy
User
Beiträge: 1019
Registriert: Montag 30. Januar 2012, 16:38

Hallo Hyperion,
will wirklich dynamisch bleiben!
Nachdem ich mehrmals die letzten Posts gelesen habe, denke ich daß ich danke Deinen Tipps das umsetzen kann.
Was mich noch beschäftigt, ist wie ich diese Logik irgend wie in die Menüdefinition einbauen kann, aber das wird meine Hausaufgabe sein. :)
Wird zwar etwas Zeit in Anspruch nehmen ..., du weißt ja ... bin nicht der Schnellste. :wink:
Nobuddy
User
Beiträge: 1019
Registriert: Montag 30. Januar 2012, 16:38

Hallo zusammen,
hoffe meine Hausaufgaben gemacht zu haben ... :)

Das dynamische Konzept von Hyperion, habe ich beibehalten.
Die Funktion 'handle_menu' und 'menu' selbst, habe ich angepasst.
Es wird ein Parameter 'menupoint' an die Funktionen weitergegeben, damit kann ich von der gewählten Hauptfunktion aus weitere Funktionen ansprechen, die anhand des Parameters unterschiedliche Aufgaben ausführen können und wieder zur Hauptfunktion zurückkehren.
Ich habe noch ein paar zusätzliche Funktionen eingebaut, um den Gesamtcode zu reduzieren.
Weiter habe ich globale Parameter erstellt, die Änderungen im Code selbst überflüssig machen.

Wie Ihr mich kennt, gibt es bestimmt die Möglichkeit den Code auch anderst zu gestalten, hoffe daß Ihr mir dabei etwas helft. :wink:

Poste hier mal den Code mit einer Beispieldatei: https://gist.github.com/3246785

Wenn der Code Euch zu heiß ist ..., trinkt einfach ein kühles Bier. :wink:
deets

Es waere wirklich toll, wenn du endlich mal all den unnoettigen Code der Form

Code: Alles auswählen

if <bedingung>:
     tu_was()
     tu_dasselbe()
else:
     tu_was_anderes()
     tu_dasselbe()
umformen wuerdest zu

Code: Alles auswählen

if <bedingung>:
   tu_was()
else:
   tu_was_anderes()
tu_dasselbe()
Und schwups wird das ganze deutlich kuerzer und besser.
Nobuddy
User
Beiträge: 1019
Registriert: Montag 30. Januar 2012, 16:38

@deets, das Einzige was ich gefunden habe, war hier:

Code: Alles auswählen

def search_entry(menupoint):
    while True:
        entry = input('\nSuchbegriff: ')
        if entry:
            print("\nListe wird nach Suchbegriff durchsucht!")
            with open(datapool, 'r') as infile:
                reader = csv.reader(infile, delimiter="\t", quotechar="^")
                data = list()
                for item in reader:
                    if item[0] == entry:
                        line = item[0], item[1], item[2]
                        data.append(line)
                    elif entry in str(item):
                        line = item[0], item[1], item[2]
                        data.append(line)
            if not data:
                print('\nEs liegen keine Ergebnisse zu dem Suchbegriff %s vor\n' % entry)
                break
            else:
                selection(menupoint, data)
                break
hier habe ich dies Passage so geändert:

Code: Alles auswählen

                for item in reader:
                    if item[0] == entry:
                        line = item[0], item[1], item[2]
                    elif entry in str(item):
                        line = item[0], item[1], item[2]
                    data.append(line)
Wenn Du noch weiteren solchen Code bei mir gefunden hast, bitte kurz posten!
BlackJack

@Nobuddy: Über den Code denkst Du bitte noch einmal nach. Zum Beispiel warum das zwei verschiedene Zweige sind. Und dann nenne mal bitte Werte für `entry` und `item` bei dem die zweite Bedingung wahr ist, die erste aber nicht.
deets

er ist quasi ueberall, ich muesste das ganze ding posten.

Und das, was du gerade geaendert hast ist so natuerlich falsch, denn dann wird immer appended, egal, was passiert. Du darfst aber nur appenden, wenn if oder elif laufen. Natuerlich laesst sich das trotzdem verbessern:

Code: Alles auswählen

if item[0] == entry or entry in str(item):
    ...
Dann if not data ... break - es wird im if und im else ge-break-ed, also kann break dahinter stehen.

if max(countlist)... ist genau so ein fall.

Das ist doch wirklich simpel das beim drueberlesen zu erkennen!!
Nobuddy
User
Beiträge: 1019
Registriert: Montag 30. Januar 2012, 16:38

Ok, also mal zu der Funktion 'search_entry'.
Die Liste habe ich so aufgebaut, daß in der ersten Spalte Begriffe zu Python2, in der zweiten Spalte Begriffe zu Python3 und in der dritten Spalte ein Info dazu steht.
Ich suche zuerst einmal in der ersten Spalte mit dem Python2-Begriff also 'item[0]'.
Finde ich in 'item[0]' nichts, so durchsuche ich die komplette Zeile des Datensatzes.
Es ist also möglich eine 1:1 Suche in der ersten Spalte, wie auch eine Suche über einen Teilbegriff im Datensatz zu suchen.

Die Suche funktioniert so, wie ich es mir vorgestellt habe.
Ihr solltet es einfach mal ausprobieren, die Textdatei habe ich ja als Beispiel dazu gelegt.

Ich will damit nicht behaupten, daß mein Code perfekt ist, aber zumindest funktioniert er einwandfrei.
Ich habe ja dazu geschrieben, daß ich mich auf Hilfe freue, den Code zu verbessern!
deets hat geschrieben:Dann if not data ... break - es wird im if und im else ge-break-ed, also kann break dahinter stehen.
Kannst Du mir mal ein Beispiel aufzeigen? Mir ist nicht klar, wie Du dies meinst!
BlackJack hat geschrieben:@Nobuddy: Über den Code denkst Du bitte noch einmal nach. Zum Beispiel warum das zwei verschiedene Zweige sind. Und dann nenne mal bitte Werte für `entry` und `item` bei dem die zweite Bedingung wahr ist, die erste aber nicht.
Was meinst Du mit zwei verschiedene Zweige?

Wenn es Euch weiterhilft, kann ich Euch ja den Funktionsablauf erklären. :wink:
Zuletzt geändert von Nobuddy am Freitag 3. August 2012, 17:58, insgesamt 1-mal geändert.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Nobuddy hat geschrieben: Wenn es Euch weiterhilft, kann ich Euch ja den Funktionsablauf erklären. :wink:
Das würde *Dir* vor allem weiterhelfen... ;-) Anderen etwas erklären deckt Schwächen und Fehler schnell auf.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Nobuddy
User
Beiträge: 1019
Registriert: Montag 30. Januar 2012, 16:38

Ok, werde einen Funktionsablauf beschreiben, damit es Euch klarer wird und Ihr mir dadurch besser helfen könnt!
Wird aber erst am Wochenende passieren, komme vorher nicht mehr dazu.

Grüße Nobuddy
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Nobuddy hat geschrieben:Ok, werde einen Funktionsablauf beschreiben, damit es Euch klarer wird und Ihr mir dadurch besser helfen könnt!
Ich glaube, du unterschätzt hier wieder unsere pädagogische Qualifikation: wir verstehen schon was dein Code macht. Du sollst uns nur erklären was dein Code macht, damit du selber darauf kommst was er tatsächlich macht und wo die Schwächen liegen.

Hmm, vielleicht hätte ich das jetzt nicht verraten sollen :)
Das Leben ist wie ein Tennisball.
deets

Nobuddy hat geschrieben:
deets hat geschrieben:Dann if not data ... break - es wird im if und im else ge-break-ed, also kann break dahinter stehen.
Kannst Du mir mal ein Beispiel aufzeigen? Mir ist nicht klar, wie Du dies meinst!
Das Beispiel habe ich dir schon in meiner ersten Antwort hier gegeben, und nochmal Bezug genommen auf deinen Kommentar 'das war alles, was ich finden konnte' - und das war (wie BJ auch noch bemerkte, und ich ebenfalls) auch noch falsch.

Also nochmal bitte meine & BlackJacks Anworten durchlesen und deinen Code daraufhin betrachten. Mehr "Prosa" brauchen wir hier nicht, wir sehen, was dein Code alles macht (und nicht, und kompliziert, und so).
BlackJack

@Nobuddy: Ad „zwei verschiedene Zweige”: Wikipedia zu bedingten Anweisungen und Verzweigungen.

Du musst uns nicht den Funktionsablauf selbst erklären — den können wir ja in Form von Quelltext jetzt schon lesen — sondern warum Du das so machst. Zusammen mit den zusätzlichen Fragen fällt Dir dann vielleicht auf warum das teilweise wenig sinnvoll ist *was* Du da machst oder zumindest umständlich.

Werte für `entry` und `item` bei denen die zweite Bedingung wahr ist, die erste aber nicht, kann man übrigens sogar finden. Das liegt daran dass Du im Quelltext nicht das machst was Du in Prosa behauptest zu tun. Du schaust in der zweiten Bedingung nicht nach ob `entry` im gesamten Datensatz enthalten ist, sondern ob es in einer Zeichenkettendarstellung des selben enthalten ist. Das ist nicht das selbe. Denn zum einen werden nicht alle Sachen gefunden die im Datensatz enthalten sein können, da ``s == repr(s)`` nicht für alle möglichen `s` vom Typ `str` gilt und zum anderen sind in der Zeichenkettendarstellung des Datensatzes Teilzeichenketten enthalten die nicht in den einzelnen Teilen enthalten sein müssen. Was zum nachdenken:

Code: Alles auswählen

In [50]: a = 'Spam'

In [51]: b = 'Hällö'

In [52]: xs = [a, b]

In [53]: a in str(xs)
Out[53]: True

In [54]: b in str(xs)
Out[54]: False

In [55]: ',' in str(xs)
Out[55]: True

In [56]: ',' in a
Out[56]: False

In [57]: ',' in b
Out[57]: False
Nobuddy
User
Beiträge: 1019
Registriert: Montag 30. Januar 2012, 16:38

Hallo zusammen,
mir liegt es fern, an Eurer Qualifikation zu zweifeln, bitte meine Aussagen diesbezüglich nicht missverstehen! :wink:

Ich denke, es hilft Euch weiter, wenn ich Euch etwas kurz zu meiner Person mitteile.
Ich komme aus dem Maschinenbau und war zuletzt ca. 8 Jahre selbständig im Bereich Büro- und EDV-Bedarf. Hier betreibe ich noch heute einen Internet-Shop.
Seit einigen Jahren habe ich eine psychischen Erkrankung und vieles was vorher normal und einfach war, ist es auf einmal nicht mehr. Vielleicht sehe ich daher alles zu kompliziert ...
Aus persönlichem Interesse und um meiner Erkrankung entgegen zu wirken, lerne und arbeite ich mit Python und es macht mir richtig Spaß.
So viel zu einem kurzen Einblick, aber BITTE ich wünsche deshalb keine Sonderbehandlung!

@deets, habe auch festgestellt, daß meine gepostete Änderung zu 'if else' falsch war und habe sie wieder rückgängig gemacht.
Vielleicht komme ich noch selber drauf, was diesbezüglich einfacher sich in meinem Code gestalten lässt.

@BlackJack, werde mir Deinen Link durchlesen.
Deinem Code zum Nachdenken, hier wie es bei mir funktioniert:

Code: Alles auswählen

In [1]: a = 'Spam'

In [2]: b = 'Hallo'

In [3]: xs = a, b

In [4]: a in str(xs)
Out[4]: True

In [5]: b in str(xs)
Out[5]: True

In [6]: ',' in str(xs)
Out[6]: True

In [7]: ',' in a
Out[7]: False

In [8]: ',' in b
Out[8]: False

In [9]: type(xs)
Out[9]: tuple

In [10]: xs = [a, b]

In [11]: type(xs)
Out[11]: list

In [12]: xs = (a, b)

In [13]: type(xs)
Out[13]: tuple
Das mit dem Komma, ist allerdings bei beiden Ausgaben gleich!?

Zum Funktionsablauf, warum ich das so mache.
Im Hauptmenü gibt es Positionen, die auch von anderen Positionen des Hauptmenüs Verwendung finden.
Suche ich nur nach einem Begriff oder möchte einen neuen Eintrag hinzufügen oder nur die Basisliste sortieren, so reichen die Funktionen 'search_entry', 'add_entry' bzw. 'sort' völlig dafür.
Möchte ich einen oder mehrere Einträge ändern, wird zuerst die Funktion 'load' aufgerufen. Dort wird überprüft, ob die Datei chance.txt existiert. Da diese Datei jedesmal bei Nutzung des Hauptmenüs gelöscht wird, ist dies nicht der Fall und so wird die Suchfunktion gestartet, um nach dem Eintrag (Einträge) zu suchen. Evtl. erhalte ich einige ähnliche Suchergebnisse z.B. 5 Stück aufgezeigt, möchte aber nur eins oder zwei dieser Einträge ändern. Hier habe ich die zusätzliche Funktion 'selection' erstellt, mit der ich die Suchergebnisse mir anzeigen lassen kann und anhand von einem Index, mir die entsprechende Zeile auswählen kann. Diese Zeile wird in die Textdatei chance.txt gespeichert. Habe ich mehr als nur ein Suchergebnis, so habe ich die Möglichkeit erneut eine entsprechende Zeile auszuwählen. Dabei benutze ich die Funktion 'switch', die es mir ermöglicht, die aktuellen neuen Datensätzen in der Auswahl mit dem Index anzuzeigen.
Ist die Auswahl abgeschlossen, wird überprüft von welchem Menü aus der Auftrag erging (Menupoint) und die entsprechende Funktion wird aufgerufen. Hier in diesem Beispiel, die Funktion 'load' die jetzt die ausgewählten Einträge mit einem Editor (bei mir kate) öffnet, um diese zu bearbeiten. (Evtl. gibt es von Python einen speziellen Editor?)
Die alten Daten der zu bearbeitenden Zeilen, wird unter 'olddata' abgespeichert.
Nach dem Bearbeiten habe ich die Möglichkeit die gemachten Änderungen zu verwerfen.
Wenn die Änderungen übernommen werden, werden aus chance.txt die Zeilen der Liste hinzugefügt und die 'alten' Zeilen gelöscht.

Das war jetzt, wenn ich einen oder mehrere Einträge ändern möchte.
Ähnlich funktioniert dies auch beim Löschen mit der Funktion 'remove_entry'.
BlackJack

@Nobuddy: Ich habe `b` an etwas anderes gebunden als Du. Mit 'Hallo' gibt es keine Probleme denn das besteht nur aus Zeichen bei denen die `repr()`-Darstellung den ursprünglichen Zeichen entspricht.

Meine Frage zum Funktionsablauf beschränkte sich eigentlich nur auf die `search_entry()`-Funktion, und da auf das ``if``/``elif`` mit dem die Datensätze geprüft werden. Was hast Du Dir bei den beiden Bedingungen gedacht, was erfasst die erste und was die zweite, und was beide?
Antworten