UnboundLocalError: local variable 'x' referenced before

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
JanInfoHD
User
Beiträge: 16
Registriert: Montag 17. August 2015, 17:48

Ständige Fehlermeldung :UnboundLocalError: local variable 'x' referenced before assignment :cry:
Ich bin Programmieranfänger in Python, und hatte vor auf eigene Faust ein Programm zu programmieren, das nach jedem ausführen die Zahl in der .txt Datei um einen addiert:

Code: Alles auswählen

import os
def Counter():
    if os.path.exists("lastID.txt") == False:
        datei=open("lastID.txt","w")
        print("Die Datei 'lastID' wurde erstellt!")
        datei.write("0")
        Counter()
    else:
        print("Die Datei 'lastID.txt' ist vorhanden !")
        datei=open("lastID.txt","r")
        for x in datei.readlines():
            datei.close()
        datei=open("lastID.txt","w")
        datei.write(x = x+1)
        datei.close()
        print("Datei 'lastID.txt'beschrieben:",x)
              
Counter()
Ich hoffe ihr könnt mir helfen ;D
MfG JanInfoHD

Anhang: (Konsolen ausgabe)
Die Datei 'lastID.txt' ist vorhanden !
Traceback (most recent call last):
File "C:/Users/Benutzer/Desktop/Benutzer/last-ID.py", line 18, in <module>
Counter()
File "C:/Users/Benutzer/Desktop/Benutzer/last-ID.py", line 14, in Counter
datei.write(x = x+1)
UnboundLocalError: local variable 'x' referenced before assignment
Zuletzt geändert von cofi am Mittwoch 19. August 2015, 10:54, insgesamt 3-mal geändert.
Grund: Urspruenglicher Tittel wiederhergestellt
JanInfoHD
User
Beiträge: 16
Registriert: Montag 17. August 2015, 17:48

PS: Das sind keine Hausaufgaben oder ähnliches, Ich komme nur nicht weiter, deswegen frage ich :K
BlackJack

@JanInfoHD: Das passiert wenn Du einen lokalen Namen verwenden willst bevor er definiert wurde. Falls Du denkst `x` müsste durch die ``for``-Schleife definiert sein: Was für einen Wert sollte `x` denn haben wenn die Datei leer ist?
JanInfoHD
User
Beiträge: 16
Registriert: Montag 17. August 2015, 17:48

BlackJack hat geschrieben:@JanInfoHD: Das passiert wenn Du einen lokalen Namen verwenden willst bevor er definiert wurde. Falls Du denkst `x` müsste durch die ``for``-Schleife definiert sein: Was für einen Wert sollte `x` denn haben wenn die Datei leer ist?
Der WErt soll erstmals bei null sein damit bei jedem ausführen der Definition Die Zahl um 1 addiert wird
Dann müsste ich oben x = 0 hinschreiben :DDann habe ich aber den gleichen Fehler
P.S Danke schonmal für die Antwort :D
Zuletzt geändert von JanInfoHD am Montag 17. August 2015, 18:14, insgesamt 1-mal geändert.
JanInfoHD
User
Beiträge: 16
Registriert: Montag 17. August 2015, 17:48

@BlackJack wenn ich die Variable dierekt erstelle (Ich habe dies dierekt nach dem import des moduls os gemacht), wird der gleiche Fehler angezeigt
BlackJack

@JanInfoHD: Wenn Du oben beim Import `x` definierst dann ist das ein `x` das auf Modulebene existiert aber nicht das selbe ist wie der lokale Name `x` innerhalb der Funktion.

Teil das Problem doch mal in kleinere Teilschritte auf die Du jeweils durch eine eigene Funktion löst. Die dann testen ob sie tatsächlich funktioniert. Und am besten erst mit der nächsten Funktion weitermachen wenn man eine fertig hat und weiss das die funktioniert, eben weil man sie mit verschiedenen Werten/Szenarien getestet hat.

Eine mögliche Aufteilung wäre beispielsweise eine Funktion welche die Datei liest und deren Inhalt als Zahl liefert oder 0 falls die Datei nicht existiert. (Und in der Funktion die Datei dann *nicht* anlegen!)

Und dann eine Funktion die eine Zahl in eine Datei schreibt.

Die beiden Funktionen kann man dann in einer Funktion verwenden die lesen, erhöhen, und schreiben verbindet.

Programmieren bedeutet oft Probleme in kleinere Teilprobleme zu zerlegen. Und diese Teilprobleme dann auch wieder zerlegen, solange bis man Teilprobleme hat, die sich leicht in wenigen Zeilen Code in einer Funktion lösen lassen. Die dadurch gewonnen Teillösungen testet man dann und wenn sie funktionieren, setzt man sie zu grösseren Teillösungen zusammen, bis man das Gesamtproblem erschlagen hat.

Weitere Anmerkungen: Zur Namenschreibweise und Quelltextformatierung lohnt sich ein Blick in den Style Guide for Python Code. Die Funktion sollte beispielweise klein geschrieben werden, denn so sieht der Name nach einer Klasse aus.

Vergleiche mit literalen Wahrheitswerten macht man nicht, denn da kommt ja sowieso nur wieder ein Wahrheitswert heraus. Wenn man den Test negieren möchte, dann geht das mit ``not``, also ``if not os.path.exist('lastID.txt'):``.

Dateien die man öffnet sollte man auch wieder schliessen. Genau daher kommt nämlich Dein Problem: die '0' wird nicht in die Datei geschrieben weil die meisten Laufzeitbibliotheken die Dateioperationen anbieten Daten erst im RAM puffern bis genug für einen sinnvollen Schreibvorgang zusammengekommen ist, oder der Programmierer explizit sagt das der Puffer rausgeschrieben werden soll. Dies passiert auch implizit bei einem `close()`-Aufruf. Noch besser aber ist es wenn man Dateien wo das möglich ist zusammen mit der ``with``-Anweisung öffnet. Dann wird die Datei in jedem Fall geschlossen wenn der ``with``-Block verlassen wird, also auch wenn da mittendrin ein ``return`` steht, oder der Block durch eine Ausnahme verlassen wird.

Das „Don't Repeat Yourself“-Prinzip (DRY) sagt das sich Code und Daten nicht wiederholen sollten. Der Dateiname sollte da nur *einmal* im Quelltext stehen. Zum Beispiel als Konstante definiert oder der Funktion als Argument übergeben. Sonst muss man den Quelltext an vielen Stellen ändern wenn man den Namen mal anpassen möchte und dabei besteht dann die Gefahr das man eine Stelle vergisst, oder nicht an allen Stellen exakt die gleiche Änderung macht.

Auf die Existenz der Datei zu testen ist in Python unüblich. Man würde hier einfach davon ausgehen das sie existiert und dann den Fall das sie nicht existiert über die Ausnahmebehandlung bearbeiten.

Ich persönlich würde Textdateien immer mit einem Zeilenendezeichen beenden. Zumindest unter Linux/Unix ist das so üblich weil viele Programme das erwarten. Also dass man beispielsweise einfach zwei Textdateien hintereinander kopieren kann ohne das an der ”Nahtstelle” dann eine Zeile existiert, die aus dem Inhalt der letzten Zeile aus der ersten Datei und dem Inhalt der ersten Zeile aus der zweiten Datei besteht.

Man muss die Datei eigentlich auch gar nicht mit einer 0 erstellen. Man kann im Falle das es die Datei nicht gibt auch `x` einfach als 0 annehmen und dann gleich mit dem erhöhen und schreiben weitermachen.

`Counter()` sollte sich nicht selbst aufrufen. Die Funktion löst kein rekursives Problem, sondern der Aufruf wird einfach nur als ”Wiederholung” missbraucht. Dabei ist das überhaupt gar nicht nötig wenn man den ``else``-Zweig weglässt und dessen Inhalt einfach nach dem ``if``-Zweig ausführen lässt!

Das ``datei.close()`` innerhalb der ``for``-Schleife ist unsinnig. `readlines()` verwendet man eigentlich auch nicht mehr, denn Dateiobjekte sind iterierbar, das heisst wenn man direkt über die Datei iteriert bekommt man die Zeilen. Falls die komplette Datei nur aus dem Zähler besteht, würde ich sie komplett einlesen, dann erkennt man auch beim Umwandeln des Inhalts in eine Zahl ob da neben dem Zähler noch irgend welche anderen Daten stehen die dort nicht hingehören.

Das Umwandeln in eine Zahl fehlt in Deinem Programm. Auf Zeichenketten kann man nicht eine 1 addieren.

Zeile 14 sieht irgendwie geraten aus. Die `write()`-Methode erwartet/kennt kein Argument mit dem Namen `x`. Das wäre dann der nächste Fehler der in der Zeile dann zur Ausnahme führt wenn ein lokales `x` definiert ist. Und man muss die um eins erhöhte Zahl dann zum schreiben in eine Datei natürlich auch wieder in eine Zeichenkette umwandeln, denn Zahlen kann man nicht in eine Datei schreiben.
JanInfoHD
User
Beiträge: 16
Registriert: Montag 17. August 2015, 17:48

BlackJack hat geschrieben:@JanInfoHD: Wenn Du oben beim Import `x` definierst dann ist das ein `x` das auf Modulebene existiert aber nicht das selbe ist wie der lokale Name `x` innerhalb der Funktion.

Teil das Problem doch mal in kleinere Teilschritte auf die Du jeweils durch eine eigene Funktion löst. Die dann testen ob sie tatsächlich funktioniert. Und am besten erst mit der nächsten Funktion weitermachen wenn man eine fertig hat und weiss das die funktioniert, eben weil man sie mit verschiedenen Werten/Szenarien getestet hat.

Eine mögliche Aufteilung wäre beispielsweise eine Funktion welche die Datei liest und deren Inhalt als Zahl liefert oder 0 falls die Datei nicht existiert. (Und in der Funktion die Datei dann *nicht* anlegen!)

Und dann eine Funktion die eine Zahl in eine Datei schreibt.

Die beiden Funktionen kann man dann in einer Funktion verwenden die lesen, erhöhen, und schreiben verbindet.

Programmieren bedeutet oft Probleme in kleinere Teilprobleme zu zerlegen. Und diese Teilprobleme dann auch wieder zerlegen, solange bis man Teilprobleme hat, die sich leicht in wenigen Zeilen Code in einer Funktion lösen lassen. Die dadurch gewonnen Teillösungen testet man dann und wenn sie funktionieren, setzt man sie zu grösseren Teillösungen zusammen, bis man das Gesamtproblem erschlagen hat.

Weitere Anmerkungen: Zur Namenschreibweise und Quelltextformatierung lohnt sich ein Blick in den Style Guide for Python Code. Die Funktion sollte beispielweise klein geschrieben werden, denn so sieht der Name nach einer Klasse aus.

Vergleiche mit literalen Wahrheitswerten macht man nicht, denn da kommt ja sowieso nur wieder ein Wahrheitswert heraus. Wenn man den Test negieren möchte, dann geht das mit ``not``, also ``if not os.path.exist('lastID.txt'):``.

Dateien die man öffnet sollte man auch wieder schliessen. Genau daher kommt nämlich Dein Problem: die '0' wird nicht in die Datei geschrieben weil die meisten Laufzeitbibliotheken die Dateioperationen anbieten Daten erst im RAM puffern bis genug für einen sinnvollen Schreibvorgang zusammengekommen ist, oder der Programmierer explizit sagt das der Puffer rausgeschrieben werden soll. Dies passiert auch implizit bei einem `close()`-Aufruf. Noch besser aber ist es wenn man Dateien wo das möglich ist zusammen mit der ``with``-Anweisung öffnet. Dann wird die Datei in jedem Fall geschlossen wenn der ``with``-Block verlassen wird, also auch wenn da mittendrin ein ``return`` steht, oder der Block durch eine Ausnahme verlassen wird.

Das „Don't Repeat Yourself“-Prinzip (DRY) sagt das sich Code und Daten nicht wiederholen sollten. Der Dateiname sollte da nur *einmal* im Quelltext stehen. Zum Beispiel als Konstante definiert oder der Funktion als Argument übergeben. Sonst muss man den Quelltext an vielen Stellen ändern wenn man den Namen mal anpassen möchte und dabei besteht dann die Gefahr das man eine Stelle vergisst, oder nicht an allen Stellen exakt die gleiche Änderung macht.

Auf die Existenz der Datei zu testen ist in Python unüblich. Man würde hier einfach davon ausgehen das sie existiert und dann den Fall das sie nicht existiert über die Ausnahmebehandlung bearbeiten.

Ich persönlich würde Textdateien immer mit einem Zeilenendezeichen beenden. Zumindest unter Linux/Unix ist das so üblich weil viele Programme das erwarten. Also dass man beispielsweise einfach zwei Textdateien hintereinander kopieren kann ohne das an der ”Nahtstelle” dann eine Zeile existiert, die aus dem Inhalt der letzten Zeile aus der ersten Datei und dem Inhalt der ersten Zeile aus der zweiten Datei besteht.

Man muss die Datei eigentlich auch gar nicht mit einer 0 erstellen. Man kann im Falle das es die Datei nicht gibt auch `x` einfach als 0 annehmen und dann gleich mit dem erhöhen und schreiben weitermachen.

`Counter()` sollte sich nicht selbst aufrufen. Die Funktion löst kein rekursives Problem, sondern der Aufruf wird einfach nur als ”Wiederholung” missbraucht. Dabei ist das überhaupt gar nicht nötig wenn man den ``else``-Zweig weglässt und dessen Inhalt einfach nach dem ``if``-Zweig ausführen lässt!

Das ``datei.close()`` innerhalb der ``for``-Schleife ist unsinnig. `readlines()` verwendet man eigentlich auch nicht mehr, denn Dateiobjekte sind iterierbar, das heisst wenn man direkt über die Datei iteriert bekommt man die Zeilen. Falls die komplette Datei nur aus dem Zähler besteht, würde ich sie komplett einlesen, dann erkennt man auch beim Umwandeln des Inhalts in eine Zahl ob da neben dem Zähler noch irgend welche anderen Daten stehen die dort nicht hingehören.

Das Umwandeln in eine Zahl fehlt in Deinem Programm. Auf Zeichenketten kann man nicht eine 1 addieren.

Zeile 14 sieht irgendwie geraten aus. Die `write()`-Methode erwartet/kennt kein Argument mit dem Namen `x`. Das wäre dann der nächste Fehler der in der Zeile dann zur Ausnahme führt wenn ein lokales `x` definiert ist. Und man muss die um eins erhöhte Zahl dann zum schreiben in eine Datei natürlich auch wieder in eine Zeichenkette umwandeln, denn Zahlen kann man nicht in eine Datei schreiben.
Vielen Dank sie haben mir echt geholfen :D
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Ich würde außerdem mal denken, wenn Du Zeilen lesen willst, solltest Du auch Zeilen schreiben, statt datei.write("0") wäre besser datei.write("0\n").
Es könnte auch daran liegen. Ausprobiert habe ich es allerdings nicht.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Ich bin kein wandelndes Lexikon, wie viele andere hier. Ich kann nur Tipps geben, was man nachsehen sollte. Ist bei readline das Newline dabei oder nicht, sodass mann es eventuell rauswerfen müßte?
BlackJack

@Alfons Mittelmeyer: Das kann man ganz leicht selbst herausfinden. Und wo der Fehler lag, hatte ich ja bereits vorher geschrieben.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

BlackJack hat geschrieben:@Alfons Mittelmeyer: Das kann man ganz leicht selbst herausfinden. Und wo der Fehler lag, hatte ich ja bereits vorher geschrieben.
Es geht darum, dass jemand, der ein Prammierer werden will, anfängt sich selbst Fragen zu stellen und nach den Anworten sucht. Außerdem muß er lernen, wie man etwas testet, ob es stimmt oder nicht. Mit Tests kann man auch die richtigen Antworten zu finden. Es nützt nichts, ein längeres Programm zu schreiben und sich dann hinterher zu wundern, warum es nicht geht. Einfach Schritt für Schritt: etwas in ein File schreiben, dann die Zeile lesen und ausgeben lassen, wie sie aussieht. Dann darauf kommen, dass es ein String ist und addieren nicht geht. Wenn man Schritt für Schritt vorgeht und dabei löst was nicht stimmt, dann läuft am Ende das Programm. Bei einem Programm im Ganzen, muss man dann wohl andere für sich suchen lassen. Aber das ist nicht der richtige Weg.
BlackJack

@JanInfoHD: Mal eine mögliche ”Musterlösung” wie man das Problem auf Teilprobleme herunterbrechen kann, die sich dann mit einfachen Funktionen lösen lassen:

Code: Alles auswählen

#!/usr/bin/env python
from __future__ import absolute_import, division, print_function
import errno

ID_FILENAME = 'lastID.txt'


def read_id(filename):
    try:
        with open(filename, 'r') as id_file:
            return int(id_file.read())
    except EnvironmentError as error:
        if error.errno == errno.ENOENT:
            return 0
        raise


def write_id(filename, value):
    with open(filename, 'w') as id_file:
        id_file.write('{0}\n'.format(value))


def increment_id(filename):
    write_id(filename, read_id(filename) + 1)
    

def main():
    increment_id(ID_FILENAME)


if __name__ == '__main__':
    main()
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

@JanInfoHD: Bitte aendere den Thread Titel nicht im Nachhinein so, dass er nichts mehr mit dem Problem zu tun hat. Das macht es nur anderen mit dem gleichen Problem schwieriger.
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Die `write_id()` würde ich sogar weglassen und die Funktion zum Erhöhen der ID so schreiben:

Code: Alles auswählen

def increment_id(filename):
    with open(filename, 'w') as id_file:
        print(read_id(filename) + 1, file=id_file)
BlackJack

@snafu: Das wird so nicht gehen weil das `open()` zum schreiben den Inhalt der Datei löscht und damit das `read_id()` nicht funktioniert.
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Stimmt. Ich hatte es nicht getestet, sonst wäre mir sicherlich aufgefallen, dass es hier von Bedeutung ist, in welcher Reihenfolge die Datei für die verschiedenen Operationen geöffnet wird. Daher hier die Korrektur:

Code: Alles auswählen

def increment_id(filename):
    new_id = read_id(filename) + 1
    with open(filename, 'w') as id_file:
        print(new_id, file=id_file)
Antworten