Seite 1 von 1

UnboundLocalError :(

Verfasst: Sonntag 19. April 2020, 17:05
von LauLauGaamer
Hallo,
Ich programmiere jetzt seit ca 1 und einer halben Woche mit Python und das Problem ist, dass ich die ganze Zeit einen Fehler (UnboundLocalError).
Ich weiß so ziemlich worum es sich bei dem Fehler handelt, jedoch passt das nicht bei mir ins Script rein, denn ich erstelle die Variable ganz am Anfang des Codes. Es handelt sich um die Variable indexOfBack, die einen Fehler wirft, sobald man sie mit Back() versucht abzufragen
Error:
UnboundLocalError Traceback (most recent call last)
<ipython-input-32-70f170e79fd9> in <module>
50
51 if command == "Key.cmd":
---> 52 back()
53 Other(command)
54 win = True

<ipython-input-32-70f170e79fd9> in back()
45
46 def back():
---> 47 if indexOfBack > 0:
48 txt.write("\n[Backspace x" + str(indexOfBack) + "]\n")
49 indexOfBack = 0

UnboundLocalError: local variable 'indexOfBack' referenced before assignment

Code: Alles auswählen

#CAPS Lock wird nicht beachtet

caps = False
strg = False
alt = False
win = False
backspace = False
indexOfBack = 0

with open("./log.txt","r") as file:
	for line in file:
		line = line.strip()
		line = line.split(";")

		if len(line) == 2:
			command = line[1]
			time = line[0]

			command = command.replace("'","")
			command = command.strip()

			time = time.replace("[","")
			time = time.replace("]","")
			time = time.split(":")
			time = time[1]

			with open("./log_Auswertung.txt", "a+") as txt:

				def Other(key):
					if strg == True and str(key) != "Key.ctrl_l" or strg == True and str(key) != "Key.ctrl_r" or alt == True and str(key) != "Key.alt_l" or alt == True and str(key) != "Key.alt_r" or win == True and str(key) != "Key.cmd":
						if str(key) == "Key.cmd":
							txt.write(" + GUI")
						elif str(key) == "Key.ctrl_r" or str(key) == "Key.ctrl_l":
							txt.write(" + STRG")
						elif str(key) == "Key.alt_r" or str(key) == "Key.alt_l":
							txt.write(" + ALT")

					elif strg == False and alt == False and win == False:
						if str(key) == "Key.cmd":
							txt.write("[GUI")
						elif str(key) == "Key.ctrl_r" or str(key) == "Key.ctrl_l":
							txt.write("[STRG")
						elif str(key) == "Key.alt_r" or str(key) == "Key.alt_l":
							txt.write("[ALT")

				def back():
					if indexOfBack > 0:
						txt.write("\n[Backspace x" + str(indexOfBack) + "]\n")
						indexOfBack = 0

				if command == "Key.cmd":
					back()
					Other(command)
					win = True
				elif command == "Key.ctrl_l" or command == "Key.ctrl_r":
					back()
					Other(command)
					strg = True
				elif command == "Key.alt_l" or command == "Key.alt_r":
					back()
					Other(command)
					alt = True
				elif command == "Key.tab":
					back()
					if strg == False and alt == False and win == False:
						txt.write("\n[TAB]\n")
				elif command == "Key.enter":
					back()
					txt.write("\n[ENTER]\n")
				elif command == "Key.caps_lock":
					if caps == True:
						caps = False
					else:
						caps = True
				elif command == "Key.backspace":
					indexOfBack += 1
				elif command == "Key.space":
					back()
					txt.write(" ")
				elif command == "Key.esc":
					back()
					if strg == False and alt == False and win == False:
						txt.write("\n[ESC]\n")
					else:
						txt.write(" + ESC")
				else:

					back()

					if command == "Key.shift":
						indexOfBack = 0
					elif command == "\x01":
						txt.write(" + A]")
						strg = False
						alt = False
						win = False
					elif command == "\x13":
						txt.write(" + S]")
						strg = False
						alt = False
						win = False
					elif command == "\x04":
						txt.write(" + D]")
						strg = False
						alt = False
						win = False
					elif command == "\x06":
						txt.write(" + F]")
						strg = False
						alt = False
						win = False
					elif command == "\x19":
						txt.write(" + Y]")
						strg = False
						alt = False
						win = False
					elif command == "\x18":
						txt.write(" + X]")
						strg = False
						alt = False
						win = False
					elif command == "\x03":
						txt.write(" + C]")
						strg = False
						alt = False
						win = False
					elif command == "\x16":
						txt.write(" + V]")
						strg = False
						alt = False
						win = False
					elif command == "\x0e":
						txt.write(" + N]")
						strg = False
						alt = False
						win = False
					elif command == "\x1a":
						txt.write(" + Z]")
						strg = False
						alt = False
						win = False
					elif command == "\x0f":
						txt.write(" + O]")
						strg = False
						alt = False
						win = False
					elif command == "\x10":
						txt.write(" + P]")
						strg = False
						alt = False
						win = False
					else:
						char = "abcdefghijklmnopqrstuvwxyzüäöABCDEFGHIJKLMNOPQRSTUVWXYZÄÖÜ<>|,;.:-_#'+*~´`?ß}=])[({/&%$§^°@€"

						found = False

						for letter in char:
							if command == letter:
								found = True
								if caps == True:
									txt.write(command.upper())
								else:
									txt.write(command)

						if found == False:
							txt.write("\n(" + command + ")\n")
print("Finished")

Re: UnboundLocalError :(

Verfasst: Sonntag 19. April 2020, 18:32
von Sirius3
Das erste, was Du verbessern solltest, ist Deine Einrücktiefe. Neune Ebenen sind eindeutig zu viel.
Funktionsdefinitionen gehören auch nicht tief verschachtelt in einer Schleife, sondern auf oberster Ebene.

Und dann sieht man auch, dass bei

Code: Alles auswählen

def back():
    if indexOfBack > 0:
        txt.write("\n[Backspace x" + str(indexOfBack) + "]\n")
        indexOfBack = 0
zwei Argumente fehlen `indexOfBack` und `txt`. `indexOfBack` wird auch geändert, muß also auch als Rückgabewert zurückgegeben werden.

Variablennamen schreibt man klein_mit_unterstrich; der Filemode "a+" ist eigentlich nie sinnvoll, das sollte "a" sein.
Man prüft nicht explizit auf True oder False, da das nur wieder einen Wahrheitswert ergibt. Bei False prüft man per ›not xyz‹.
In der Funktion `Other` ist key schon ein String, die vielen str-Aufrufe also überflüssig. Die if-Bedingung ist mir auch ohne hilfreiche Klammern zu unsicher, dass da das richtige geprüft wird.

Re: UnboundLocalError :(

Verfasst: Dienstag 21. April 2020, 13:18
von __blackjack__
@LauLauGaamer: Weitere Anmerkungen: Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst.

Wenn man die gleiche Variable nacheinander auf Gleichheit gegen feste Werte testet und das alles ``or`` verknüpft, kann man das kürzer und leichter lesbar mit ``in`` und einer Liste mit den Werten ausdrücken. Test auf Ungleichheit entsprechend mit ``not in``.

`backspace` wird definiert, aber nirgends verwendet.

Bei Textdateien sollte man immer explizit die Kodierung angeben.

Der Dateimodus für das Ergebnis sollte wahrscheinlich auch nicht "a" sein, denn das ist ziemlich ineffizient diese Datei für jede Zeile der Eingabedatei erneut zu öffnen und zu schliessen.

`txt` ist kein guter Name für ein Dateiobjekt.

`line` an eine Zeichenkette und gleich danach an eine Liste mit Zeichenketten zu binden ist verwirrend.

`time` wird nicht wirklich irgendwo verwendet.

Um einen Wahrheitswert ”umzudrehen” braucht man kein ``if``/``else``: ``caps = not caps``.

Die ganzen Tests nach dem Muster ``command == "\x??"`` und das schreiben von " + ?]" lassen sich zusammenfassen, weil sich der Ausgabebuchstabe leicht aus dem Steuerzeichen berechnen lässt. Es würde vielleicht auch Sinn machen einfach *alle* Steuerzeichen an der Stelle so zu behandeln und nicht nur die Auswahl auf die getestet wird.

Die Schleife mit dem `found` ist unnötig umständlich. Wenn man das so macht, sollte man die Schleife mit ``break`` verlassen wenn man das Zeichen gefunden hat. Aber die Schleife an sich ist überflüssig, weil es den ``in``-Operator gibt.

Die `back()`-Funktion ist vielleicht auch überflüssig. Die wird am Anfang von jedem Zweig aufgerufen mit nur zwei Ausnahmen (CAPS Lock und Backspace) — es wäre also vielleicht sinnvoller diese beiden Tasten gesondert zu behandeln und bei allen anderen die `back()`-Funktion *einmal* aufzurufen. Und da die ja nicht wirklich lang ist, kann man den Code dann auch *einmal* hin schreiben. Und auch den Aufruf von `Other()` könnte man auf eine Stelle im Code beschränken.

Ungetestet:

Code: Alles auswählen

#!/usr/bin/env python3

MODIFIER_KEYS = {
    "Key.alt_l",
    "Key.alt_r",
    "Key.cmd",
    "Key.ctrl_l",
    "Key.ctrl_r",
}
#
# TODO Split this up into smaller functions.
#
def main():
    # CAPS Lock wird nicht beachtet.
    caps = strg = alt = win = False
    backspace_count = 0
    #
    # FIXME Most likely not the correct encoding.
    #
    with open("./log.txt", "r", encoding="ascii") as lines:
        #
        # TODO Should file mode be "w" here?
        #
        with open("./log_Auswertung.txt", "a", encoding="utf-8") as out_file:
            for line in lines:
                try:
                    _time, command = line.strip().split(";")
                except ValueError:
                    pass  # Ignore lines with more or less than two parts.
                else:
                    command = command.replace("'", "").strip()

                    if command == "Key.caps_lock":
                        caps = not caps
                    elif command == "Key.backspace":
                        backspace_count += 1
                    else:
                        if backspace_count > 0:
                            out_file.write(
                                f"\n[Backspace x{backspace_count}]\n"
                            )
                            backspace_count = 0

                        if command in MODIFIER_KEYS:
                            if (
                                (
                                    strg
                                    and command
                                    not in ["Key.ctrl_l", "Key.ctrl_r"]
                                )
                                or (
                                    alt
                                    and command
                                    not in ["Key.alt_l", "Key.alt_r"]
                                )
                                or (win and command != "Key.cmd")
                            ):
                                if command == "Key.cmd":
                                    out_file.write(" + GUI")
                                elif command in ["Key.ctrl_r", "Key.ctrl_l"]:
                                    out_file.write(" + STRG")
                                elif command in ["Key.alt_r", "Key.alt_l"]:
                                    out_file.write(" + ALT")

                            elif not (strg or alt or win):
                                if command == "Key.cmd":
                                    out_file.write("[GUI")
                                elif command in ["Key.ctrl_r", "Key.ctrl_l"]:
                                    out_file.write("[STRG")
                                elif command in ["Key.alt_r", "Key.alt_l"]:
                                    out_file.write("[ALT")

                        if command == "Key.cmd":
                            win = True
                        elif command in ["Key.ctrl_l", "Key.ctrl_r"]:
                            strg = True
                        elif command in ["Key.alt_l", "Key.alt_r"]:
                            alt = True
                        elif command == "Key.tab":
                            if not (strg or alt or win):
                                out_file.write("\n[TAB]\n")
                        elif command == "Key.enter":
                            out_file.write("\n[ENTER]\n")
                        elif command == "Key.space":
                            out_file.write(" ")
                        elif command == "Key.esc":
                            if strg or alt or win:
                                out_file.write(" + ESC")
                            else:
                                out_file.write("\n[ESC]\n")
                        else:
                            #
                            # TODO Redundant because `backspace_count` is
                            # already zero.
                            #
                            if command == "Key.shift":
                                backspace_count = 0
                            #
                            # TODO Why not handling every control character
                            # between 1 and 31 like this?
                            #
                            elif command in (
                                "\x01\x13\x04\x06\x19\x18"
                                "\x03\x16\x0e\x1a\x0f\x10"
                            ):
                                character = chr(ord(command) + ord("@"))
                                out_file.write(f" + {character}]")
                                strg = alt = win = False
                            else:
                                if command in (
                                    "abcdefghijklmnopqrstuvwxyzüäö"
                                    "ABCDEFGHIJKLMNOPQRSTUVWXYZÄÖÜ"
                                    "<>|,;.:-_#'+*~´`?ß}=])[({/&%$§^°@€"
                                ):
                                    if caps:
                                        command = command.upper()
                                    out_file.write(command)
                                else:
                                    out_file.write(f"\n({command})\n")
    print("Finished")


if __name__ == "__main__":
    main()
Wie Sirius3 schon anmerkte ist das alles zu viel und zu tief verschachtelt und sollte sinnvoll auf Funktionen aufgeteilt werden. Man könnte den Zustand der Modifier-Tasten eventuell auch mit `enum.Flags` zu einem Wert zusammenfassen.