return innerhalb und ausserhalb einer while-Schleife

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
inips
User
Beiträge: 5
Registriert: Freitag 8. Januar 2021, 10:42

Hallo

Ich habe ein Problem mit einer while-Schleife: Die Liste, die ich zuvor erstelle und dann innerhalb von while ergänze, kann anschliessend nicht ausserhalb von while verwendet werden. Hier der Code:

Code: Alles auswählen

#! C:\Python\Python39\python.exe -u

from flask import Flask, url_for, request, render_template

app = Flask(__name__)

@app.route("/form_question_add_mc")
def form_question_add_mc():
    return render_template('form_question_add_mc.html')  # Template einbinden


@app.route("/question_add_mc_save", methods=['POST'])  # nur POST erlaubt
def question_add_mc_save():
    status = request.form['status']
    qtype = request.form['qtype']
    complexity = request.form['complexity']
    max_score = request.form['max_score']
    qtext = request.form['qtext']
    answer1 = request.form['answer1']
    img1 = request.form['img1']
    correct1 = request.form['correct1']
    explication1 = request.form['explication1']

    # Erste Antwort in zweidimensionales Array (Liste) answers schreiben
    answers=[[answer1, img1, correct1, explication1]]

    # Auf weitere Antworten prüfen
    answer_nr=2
    while answer_nr > 0: # Laufe bis zum Abbruch
        # zum Überprüfen von POST-Variablen können request.data und request.form verwendet werden
        if 'answer'+str(answer_nr) in request.form:
            answers += [[request.form['answer'+str(answer_nr)], request.form['img'+str(answer_nr)], request.form['correct'+str(answer_nr)], request.form['explication'+str(answer_nr)]]]
            # Das funktioniert:
            #return str(answers)
            answer_nr=answer_nr+1
        else:
            break
    # ***********************************************************************************************************


    # Die Antwort sollte z.B. so aussehen: [['Ja', 'pictures/img1.jpg', '1', 'Ist doch klar'], ['Manchmal', 'pictures/img2.jpg', '0', 'Nur, wenn man es nicht bemerkt'], ['Nein', 'pictures/img3.jpg', '1', 'Ich würde mal googeln...']]
    # Das funktioniert nicht
	return str(answers)


if __name__ == '__main__':
    app.run(port=2000, debug=True, use_debugger=True, use_reloader=False)
Vom Formular /form_question_add_mc kommen die Daten, in /question_add_mc_save werden sie per POST übernommen. Sicher vorhanden sind nur answer1, img1, correct1 und explication1, und die werden in die Liste answers geschrieben. In einer while-Schleife sollen, wenn vorhanden, answer2, img2, correct2 und explication2 usw. ergänzt werden. Das scheint einerseits zu klappen, denn innerhalb von while führt return str(answers) zur korrekten Anzeige der vollständigen Liste. Sobald ich aber return str(answers) nach while ausführe, erhalte ich einen Fehler:
werkzeug.exceptions.BadRequestKeyError: 400 Bad Request: The browser (or proxy) sent a request that this server could not understand.
KeyError: 'correct3'

Ich dachte, das Problem könnte mit globalen und lokalen Variablen zusammenhängen, aber da answers vor while definiert wird, sollte die Variable doch global sein, oder nicht? Kann mir jemand einen Tipp geben, was ich an meinem Ansatz ändern muss?

Gruss, inips
Bl3nder
User
Beiträge: 139
Registriert: Freitag 3. Januar 2020, 17:07

Puhhh ok also ich Versuche dir mal zu helfen obwohl Ich selber auch noch Anfänger bin.

Deine Variablen sind doch immer in einer Funktion wieso sollten Sie dann global sein ? Also damit Ich nicht ganz falsch liegen kannst du ja mal innerhalb deiner Funktionen print(locals) machen ... dort wird dann ein dictonary zurückgegeben mit den Localen Variablen ...
Ich habe ein Problem mit einer while-Schleife: Die Liste, die ich zuvor erstelle und dann innerhalb von while ergänze, kann anschliessend nicht ausserhalb von while verwendet werden. Hier der Code:
Desweiteren sieht es für mich so aus als ob dein return falsch plaziert ist ( nach dem die Schleife per break verlassen wird .... kommt nichts mehr ) Ich glaube aufgrund deines Kommentars # *********************************************************************************************************** ... zeigt dir deine IDE das nicht vernünftig an ( wie gesagt Ich kann mich aber auch irren.
Eine Vision ohne Aktion bleibe eine Illusion
bb1898
User
Beiträge: 216
Registriert: Mittwoch 12. Juli 2006, 14:28

Geltungsbereich der Variablen: @Bl3nder hat recht, die sind alle lokal - bezogen auf die Funktion. Eine Schleife oder sonstige Kontrollstruktur erzeugt keinen neuen Geltungsbereich (da verhalten sich verschiedene Sprachen verschieden).

Dein erstes "return str(answers)", von dem Du sagst, es funktioniert, bricht die Schleife ab, sobald answer_nr = 2 überprüft wurde. Weiter geht es dann nicht, ganz egal, was noch alles im Formular steht.

Wenn Du noch mal auf die Einrückung des zweiten "return str(answers)" schaust, wirst Du feststellen, dass es schon noch zur while-Schleife gehört. Es wird gerade dann ausgeführt, wenn die Schleife nicht per "break" verlassen wurde - nach dem Erhöhen von answer_nr, aber vor der Überprüfung dieser Nummer. Nach mehrfacher Lektüre der Fehlermeldung: bist Du sicher, dass mit existierendem Schlüssel "answer3" auch "correct3" vorhanden sein muss?

Noch zwei Randbemerkungen: Der return-Befehl tut in beiden Fällen, was Du ihm sagst, in diesem Sinne "funktioniert" er. Er sollte aber wirklich besser entweder nach dem Schleifenende ausgeführt werden (also rausrücken, in eine Fluchtlinie mit dem "while") oder aber an Stelle von "break". Also an der Stelle nicht nur aus der Schleife aussteigen, sondern ganz aus der Funktion. Vorausgesetzt, die Funktion soll danach wirklich nichts anderes mehr tun.

Und die Konstruktion 'answer' + str(answer_nr) ist unschön, das geht besser: f'answer{answer_nr}', 'answer{}'.format(answer_nr), 'answer%d' % answer_nr (dies letzte ist ältlich, aber benutzbar). Alles ohne ausdrückliche Umwandlung von Zahl zu Zeichenkette; und richtig nützlich und übersichtlich, wenn mehr als eine Variable in ein Stück Text eingebaut werden soll.
Benutzeravatar
__blackjack__
User
Beiträge: 14053
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Tabs und Leerzeichen mischen ist halt 💩. Nur mit Leerzeichen einrücken, keine Tabs. Dann passiert so etwas nicht. 🙂
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
Benutzeravatar
__blackjack__
User
Beiträge: 14053
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@inips: Weitere Anmerkungen: Die ``while``-Bedingung ist irreführend denn da wird nicht wirklich etwas sinnvolles geprüft. Die Bedingung ist immer wahr. Da kann man auch einfach `True` hinschreiben. Aber eigentllich ist das eine Schleife über ganze Zahlen für `answer_nr`, was `answer_no` heissen sollte, oder man kürzt es nicht ab. Und warum wird der erste Eintrag von `answers` anders behandelt als die anderen?

``+=`` ist bei Listen ein Synonym für die `extend()`-Methode und der falsche Weg um *ein* Element an eine Liste anzuhängen. Dafür ist `append()` vorgesehen.

Code: Alles auswählen

#!C:\Python\Python39\python.exe -u
from itertools import count

from flask import Flask, request, render_template

app = Flask(__name__)


@app.route("/form_question_add_mc")
def form_question_add_mc():
    return render_template("form_question_add_mc.html")


@app.route("/question_add_mc_save", methods=['POST'])
def question_add_mc_save():
    answers = []
    for answer_number in count(1):
        try:
            answers.append(
                [
                    request.form[f"{key}{answer_number}"]
                    for key in ["answer", "img", "correct", "explication"]
                ]
            )
        except KeyError:
            break
    #
    # Die Antwort sollte z.B. so aussehen:
    #
    # [
    #     ['Ja', 'pictures/img1.jpg', '1', 'Ist doch klar'],
    #     [
    #         'Manchmal',
    #         'pictures/img2.jpg',
    #         '0',
    #         'Nur, wenn man es nicht bemerkt',
    #     ],
    #     ['Nein', 'pictures/img3.jpg', '1', 'Ich würde mal googeln...'],
    # ]
    #
    return str(answers)


if __name__ == "__main__":
    app.run(port=2000, debug=True, use_debugger=True, use_reloader=False)
Die Zeichenkettendarstellung von Python-Listen sind zur Fehlersuche gedacht, nicht um die irgendwo hin zu schicken wo sie weiterverarbeitet werden.
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
inips
User
Beiträge: 5
Registriert: Freitag 8. Januar 2021, 10:42

Danke für die vielen Tipps.

@Bl3nder: "print(locals)" brachte nichts. Mir scheint der print-Befehl hier einfach ignoriert zu werden. Hingegen erhalte ich eine Ausgabe mit "return locals()". Das kann sicher bei der Fehlerfindung helfen.

@bb1898: Das zweite "return str(answers)" ist im Original nicht so eingerückt, gehört also nicht mehr zur Schleife. Ich nehme an, dass der Fehler entstanden ist, weil ich tatsächlich nicht darauf geachtet hatte, ob ich Tabs oder Leerzeichen verwendete. Werde ich in Zukunft tun.
Aussteigen aus der Funktion kann ich nicht, weil anschliessend alles noch in eine DB geschrieben werden soll. Und danke für die "Verschönerung" meiner Funktion, sieht schon etwas eleganter aus. :)

@__blackjack__: Tatsächlich hatte ich zuerst als Bedingung "while True", aber das gab einen Fehler. Aber dass diese Bedingung nicht sinnvoll ist, sehe ich nun auch.
Was ich nicht begreife, ist deine Aussage "Die Zeichenkettendarstellung von Python-Listen sind zur Fehlersuche gedacht, nicht um die irgendwo hin zu schicken wo sie weiterverarbeitet werden." Meine Idee war, die variable Anzahl von Antworten in eine einzige Variable zusammenzufassen, diese in ein Text-Feld in einer DB zu schreiben und beim Abfragen dann wieder in die Bestandteile zu zerlegen. Ist das ein falscher Ansatz? Oder meinst du etwas ganz anderes?

Und schliesslich noch: Bei der Fehlersuche bin ich darauf gestossen, dass ich die Formulardaten z.B. für den status mit "request.form.get('status')" oder mit "request.form['status']" auslesen kann. Die einen machen es so, die anderen so. Nach einigen Tests glaube ich, dass das Resultat nicht immer dasselbe ist. Ich weiss aber weder warum, noch sehe ich den Unterschied. Wo kann ich mich dazu einlesen bzw. nach welchen Stichworten muss ich suchen?
Benutzeravatar
__blackjack__
User
Beiträge: 14053
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@inips: Die Zeichenkettendarstellung einer Liste speichert man nicht. Nimm dafür etwas standardisiertes wie beispielsweise JSON.

Das `print()` wird sehr wahrscheinlich nicht ignoriert. Es wird natürlich nicht zum Browser geschickt, aber normalerweise landet so etwas im Log des Web- oder Anwendungsservers.

Was `request.form` für einen Datentyp hat kann man — total überraschend — in der Flask-Dokumentation nachlesen. Und da kann man sich dann auch zur Dokumentation zu diesem Datentyp weiterklicken und so letztlich bei der Dokumentation für die `get()`-Methode landen.
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
Antworten