Vorsicht Anfä.: locale variable referenced before assignment

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
Sinnentlehrt
User
Beiträge: 67
Registriert: Mittwoch 30. Januar 2013, 22:32

Hallo Leute,

leider will mir nicht einläuchten wie es hier weitergehen soll.

Ist ein Programm aus Python for Kids. Leider ist der Lösungsvorschlag aus dem Buch unvollständig.
Hier mal die Fehlermeldung:

Code: Alles auswählen

imp.reload (zahleingabe)
Welche Zahl zwischen 1 und 1000 soll ich ausschreiben?234
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/....../python/python3/kap05/zahleingabe.py", line 61, in <module>
    zahlwort(zahl)
  File "/home/...../python/python3/kap05/zahleingabe.py", line 53, in zahlwort
    else: print (h+'hundert'+e+'und'+z)
UnboundLocalError: local variable 'z' referenced before assignment
Lustigerweise läuft der folgende Code für einstellige und zweistellige Zahlen. Und die Variable 'z' stellt die Zehner bei zweistelligen Zahlen bereit, tut es aber nicht mehr bei dreistelligen. Bitte keine Häme über diese wohl leichte Frage. Was die Fehlermeldung aussagt ist mir ungefähr klar aber weil es eben bei den zweistelligen Zahlen klappt steh ich voll auf dem Schlauch.

Hier mal der Code und schon mal Danke für die Mühe im voraus:

Code: Alles auswählen

#!/usr/bin/python
# -*- coding: iso-8859-15 -*- 



def zahlwort(zahl):
	hundert = zahl // 100 #Hunderterstellen:  Ergebnis der Ganzahldivision
						# von zahl durch 100
	if hundert == 0: h = ""
	elif hundert == 1: h = "ein"
	elif hundert == 2: h = "zwei"
	elif hundert == 3: h = "drei"
	elif hundert == 4: h = "vier"
	elif hundert == 5: h = "fuenf"
	elif hundert == 6: h = "sechs"
	elif hundert == 7: h = "sieben"
	elif hundert == 8: h = "acht"
	elif hundert == 9: h = "neun"
	
	
	einer = zahl % 10 #Einerstellen: Rest bei der Division von zahl durch 10
	if einer == 1: e = "ein"
	elif einer == 2: e = "zwei"
	elif einer == 3: e = "drei"
	elif einer == 4: e = "vier"
	elif einer == 5: e = "fuenf"
	elif einer == 6: e = "sechs"
	elif einer == 7: e = "sieben"
	elif einer == 8: e = "acht"
	elif einer == 9: e = "neun"
	
	zehner = zahl // 10 #Zehnerstelle: Ergebnis der Ganzzahldivision
					# von zahl durch 10
	if zehner == 0: z =""
	elif zehner == 1: z = "zehn"
	elif zehner == 2: z = "zwanzig"
	elif zehner == 3: z = "dreissig"
	elif zehner == 4: z = "vierzig"
	elif zehner == 5: z = "fuenfzig"
	elif zehner == 6: z = "sechzig"
	elif zehner == 7: z = "siebzig"
	elif zehner == 8: z = "achtzig"
	elif zehner == 9: z = "neunzig"
	
	
	if zahl == 1: print('eins')
	elif zahl == 11: print('elf')
	elif zahl == 12: print('zwoelf')
	elif zahl < 10: print (e)
	elif zahl >= 10 and zahl <= 19: print (e+z)
	elif zahl >= 20 and zahl <= 99: print (e+'und'+z)
	else: print (h+'hundert'+e+'und'+z)
	

frage= 'Welche Zahl zwischen 1 und 1000 soll ich ausschreiben?'

zahl = input(frage)

zahl = int(zahl)
zahlwort(zahl)
Gruss,

Gutschy
Du weißt das du vergessen hast einzukaufen, wenn du dich morgens mit Geschirrspülmittel duscht.
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

Hallo Gutschy,

wie Du schon selbst gemerkt hast, gibt es bei 3stelligen Zahlen kein »z« mehr. Also solltest Du mal schauen, wann »z« mit einem Wert belegt wird, und vor allem, wann eben nicht.

PS: auch wenn man glaubt, bei langen »elif«-Ketten alle Fälle abzudecken, ist es trotzdem sinnvoll einen »else«-Zeig anzuhängen, in dem dann eine Fehlermeldung ála "kann eigentlich nicht vorkommen" ausgegeben wird.
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Etwas zum Nachdenken:

Code: Alles auswählen

>>> def test():
...     print z
...     z = 42
... 
>>> test()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in test
UnboundLocalError: local variable 'z' referenced before assignment
Der Code ist übrigens ganz schön umständlich/grausam. Wenn du beginst Dinge per Hand aufzulisten, dann machst du etwas falsch und willst eine andere Datenstruktur verwenden. Typischerweise Tupel, Listen oder Dictionaries. Die Ermittlung von ``einer`` lässt sich zum Beispiel viel einfacher gestalten:

Code: Alles auswählen

einer = ["", "ein", "zwei", "drei", ..., "neun"][zahl % 10]
Das Leben ist wie ein Tennisball.
Sinnentlehrt
User
Beiträge: 67
Registriert: Mittwoch 30. Januar 2013, 22:32

Erstmal danke für Anteilnahme.

Das 'z' nicht referenziert sein soll habe ich verstanden, aber sobald ich Zahlen größe als Hundert abfrage kann ich die Hunderter und die Einer ausgeben,aber nicht die Zehner, die aber wiederrum bei den zweistelligen Zahlen funktionieren. Und alle drei Blöcke sehen für mich gleich aus.

EyDu,

dein Beispiel macht natürlich auch Sinn für mich, aber nicht in meinem Skript. (Was es aber wohl sollte, ich weiß :( )

Sirius3, EyDu, vielleicht sehe ich es ja Morgen, aber wenn mich jemand mit der Nase draufstupsen könnte, passt mir das auch wohl.

Gute Nacht.
Du weißt das du vergessen hast einzukaufen, wenn du dich morgens mit Geschirrspülmittel duscht.
Sinnentlehrt
User
Beiträge: 67
Registriert: Mittwoch 30. Januar 2013, 22:32

Ähm ja, mir ist dann doch was aufgefallen. :oops:

Musste halt erst mal 10 Minuten die Augen zu mache. 854 //10

Gute Nacht.
Du weißt das du vergessen hast einzukaufen, wenn du dich morgens mit Geschirrspülmittel duscht.
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Und jetzt testest du mal deinen Code mit 10, 20, 100, 101, 110 und 111.
Das Leben ist wie ein Tennisball.
Sinnentlehrt
User
Beiträge: 67
Registriert: Mittwoch 30. Januar 2013, 22:32

Hi EyDu,

leider konnte ich meinen Code noch nicht soweit vorantreiben um die von dir vorgeschlagenen Tests durchzuführen, aber natürlich weiß auch was das hinausläuft.

Ich bin immer noch dabei die Ausgabe der 'Zehner' bei dreistelligen Zahlen irgendwie auszuführen. Meine Überlegungen haben dazu geführt eine eigene Variable für 'Zehner': 'h_z' einzuführen wenn sie aus dreistelligen Zahlen kommen. Leider bin ich wieder auf einer Fehlermeldung hängengeblieben.

Code: Alles auswählen

>>> imp.reload (zahleingabe)
Welche Zahl zwischen 1 und 1000 soll ich ausschreiben?258
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/...../python/python3/kap05/zahleingabe.py", line 109, in <module>
    zahlwort(zahl)
  File "/home/...../python/python3/kap05/zahleingabe.py", line 99, in zahlwort
    elif zahl >= 100: print(h +'hundert'+e+'und'+z_h)
NameError: global name 'z_h' is not defined

Ich habe eine neue Funktion geschrieben und diese dann als Unterfunktion eingebaut, ich hatte gehofft damit das 'gobal name' Problem lösen zu können. Leider ohne Erfolg.

Hier mal meine Überlegungen:

Code: Alles auswählen

def zahlwort(zahl):
	def zehner_2(h_zehner):
		zehner = h_zehner // 10 # Zehnerstelle wenn Sie aus dreistelligen Zahlen 
						# kommen.

		if zehner == 0: h_z =""
		elif zehner == 1: h_z = "zehn"
		elif zehner == 2: h_z = "zwanzig"
		elif zehner == 3: h_z = "dreissig"
		elif zehner == 4: h_z = "vierzig"
		elif zehner == 5: h_z = "fuenfzig"
		elif zehner == 6: h_z = "sechzig"
		elif zehner == 7: h_z = "siebzig"
		elif zehner == 8: h_z = "achtzig"
		elif zehner == 9: h_z = "neunzig"
	
	
	
	
	hundert = zahl // 100 #Hunderterstellen:  Ergebnis der Ganzahldivision
						# von zahl durch 100
	if hundert == 0: h = ""
	elif hundert == 1: 
		h = "ein"
		h_zehner= hundert * 100 - zahl
		zehner_2(h_zehner)
	elif hundert == 2: 
		h = "zwei"
		h_zehner= hundert * 100 - zahl
		zehner_2(h_zehner)		
	elif hundert == 3:
		h = "drei"
		h_zehner= hundert * 100 - zahl
		zehner_2(h_zehner)
	elif hundert == 4: 
		h = "vier"
		h_zehner= hundert * 100 - zahl
		zehner_2(h_zehner)
	elif hundert == 5: 
		h = "fuenf"
		h_zehner= hundert * 100 - zahl
		zehner_2(h_zehner)
	elif hundert == 6: 
		h = "sechs"
		h_zehner= hundert * 100 - zahl
		zehner_2(h_zehner)
	elif hundert == 7: 
		h = "sieben"
		h_zehner= hundert * 100 - zahl
		zehner_2(h_zehner)
	elif hundert == 8: 
		h = "acht"
		h_zehner= hundert * 100 - zahl
		zehner_2(h_zehner)
	elif hundert == 9:
		h = "neun"
		h_zehner= hundert * 100 - zahl
		zehner_2(h_zehner)
	
	
	einer = zahl % 10 #Einerstellen: Rest bei der Division von zahl durch 10
	if einer == 1: e = "ein"
	elif einer == 2: e = "zwei"
usw.....

Ich bin schon froh dass das Skript geladen wird ohne zu meckern. Natürlich bin ich davon überzeugt das es tausendmal elegantere Wege gibt, aber die erschießen sich halt mir nocht nicht.

Gruss,

Gutschy
Du weißt das du vergessen hast einzukaufen, wenn du dich morgens mit Geschirrspülmittel duscht.
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

Besser wird das Programm durch noch mehr Wiederholungen nicht. Du behandelst jetzt "zehner" also Sonderfall für jede dreistellige Zahl einzeln. Was passiert, wenn Du auch vier. fünf, sechs, ... Stellen hast, kannst Du Dir ja ausmalen. Aber Du gehst das Problem noch nicht an der Wurzel an:
beantworte einfach die Frage: Was ist bei dreistelligen Zahlen anders, als an zweistelligen. Und wenn Du das ganze auf den Kopf stellst: Was unterscheidet den hundert==0-Zweig von den restlichen? (die Antwort ist: nichts. Aber warum das so ist, solltest Du Dir noch genauer anschauen)

PS: warum Dein neuer Ansatz nicht funktioniert: die Variable 'h_z' ist eine lokale Variable für 'zehner_2' und das ist gut so, aber Funktionen sollten Rückgabewerte haben.
Sinnentlehrt
User
Beiträge: 67
Registriert: Mittwoch 30. Januar 2013, 22:32

Lieber Sirius3,

danke für deine gutgemeinten Ratschläge, ich nehme an am einfachsten wäre die Zahleingabe in ein Array, Tupel oder Liste zu packen, die Elemente zu zählen und abhängig von der Anzahl der Elemente oder deren Positionen, Funktionen aufzurufen.

Leider kann ich deinem Vorschlag erstmal nicht folge leisten, weil mich eine erschreckende Erkenntnis ereilt hat, die allerdings schon vor einigen Wochen. Das Lesen von 2500 Seiten über Python hat mir nur wenig gebracht. Und seitdem hege ich die Hoffnung dass das sklavische Abtippen der Aufgaben und das Lösen dieser wohl doch der alleinige Heilsweg sei.

Na ja, ich dachte halt echt, lies erstmal alles durch, dann kommt das schon irgendwie in deinen Kopf. Nö, das klappte schon mal nicht. Und mein Übungsbuch hat mir tatsächlich nur die Aufgabe gestellt die Zahlen von 1 bis 100 auszuschreiben und ich habe die 1000 irgendwie dazu gedichtet. Und jetzt gehe ich schnell zur nächsten Aufgabe.

Nochmal Danke für deine Ratschläge und deine Posting-Zahl lässt leicht ergründen das du wohl vielen hier mit gutem Rat beistehst, dafür von mir auch ein dickes Lob. Und falls du Denkst ich hätte kein Talent zu Programmieren, ja du hast bestimmt recht. :wink:

Und ich schreib mir hier einen Wolf war mir das natürlich auch ein bisschen Peinlich ist. Im diesen Sinne kann ich nur hoffen das du mir auch beim nächsten mal noch zu Hilfe eilst, das gilt natürlich im gleichen Maße für EyDu.

Gute Nacht,

Gutschy
Du weißt das du vergessen hast einzukaufen, wenn du dich morgens mit Geschirrspülmittel duscht.
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Sinnentlehrt hat geschrieben:Leider kann ich deinem Vorschlag erstmal nicht folge leisten, weil mich eine erschreckende Erkenntnis ereilt hat, die allerdings schon vor einigen Wochen. Das Lesen von 2500 Seiten über Python hat mir nur wenig gebracht. Und seitdem hege ich die Hoffnung dass das sklavische Abtippen der Aufgaben und das Lösen dieser wohl doch der alleinige Heilsweg sei.
Programmieren lernt man nicht durch Lesen (zumindest nicht als Anfänger) und Abtippen von Code, sondern durch Schreiben von eigenen Programmen und Verstehen von anderem Code. Am besten fängst du mit kleinen Programmen an, welche dich auch interessieren. Dann verlierst du auch nicht die Motivation, wenn mal etwas nicht funktioniert.

Eins kannst du an deinem Code aber schon mal sehr gut sehen: schreibe niemals Programme vollständig hin und teste sie erst ganz am Ende. Du musst dich Schritt für Schritt vorarbeiten und dabei die einzelnen Schritte ausgibig testen. Besonders Grenzfälle. Erst wenn alle Testes funktionieren, dann machst du mit dem nächsten Schritt weiter. In diesem Fall könntest du also erstmal eine Funktion für die Einer schreiben. Wenn das Funktioniert, dann erweiterst du auf Zehner. Und erst wenn das geht, dann erweiterst du auf Hunderter. Wenn du jeden der drei Einzelschritte in verschiedene Funktionen packst, dann ist es am Ende ganz einfach und übersichtlich.
Das Leben ist wie ein Tennisball.
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Sehe ich ähnlich wie EyDu: Abschreiben bringt verhältnismäßig wenig Erkenntnis. Man sieht dann nur die Lösung für ein Problem und kann es vielleicht noch für sich nachvollziehen. Das ist aber weit weg von einer programmiermäßigen Eigenleistung. Ich sehe das so ähnlich wie (höhere) Mathematik: Es ist zwar schön, wenn man Lösungswege nachvollziehen kann, aber tausendmal besser ist es, ohne zu gucken selbst auf die Lösung zu kommen.

Genereller Rat (der auch schon oft genug genannt wurde): Versuche immer, dein Problem möglichst in Teilprobleme zu zerlegen und dementsprechend Funktionen für diese Teilprobleme zu erstellen. So hat man mehr Überblick, kann den Code besser nachvollziehen und kann oftmals einfacher Debuggen, wenn nach einer kleinen Änderung plötzlich das ganze Programm nicht mehr richtig funktioniert.

Wenn man "blind" Code schreibt, dann sollte der immer nur für eine Funktion gelten. Erst wenn diese erwartungsgemäß funktioniert, dann sollte man sich der nächsten Funktion zuwenden. Es passiert (zumindest mir) relativ selten beim Programmieren, dass heruntergeschriebener Code auf Anhieb die Ergebnisse ausspuckt, die ich erwartet habe. Meistens sind irgendwelche Nachbesserungen nötig.

Tests sind eine hilfreiche Automatisierung, um zu überprüfen, ob nach besagter Änderung noch alles so funktioniert, wie es vorher war. Ich bin mir aber bewusst (auch aus eigener Erfahrung), dass solche Tests von einigen Leuten eher spät in der Programmentwicklung geschrieben werden und man stattdessen lieber ein paar manuelle Tests durchführt. Ist eigentlich doof, aber naja.
Sinnentlehrt
User
Beiträge: 67
Registriert: Mittwoch 30. Januar 2013, 22:32

Danke, danke, danke. :) Zum Glück habe ich ein festes Ziel. :D
Du weißt das du vergessen hast einzukaufen, wenn du dich morgens mit Geschirrspülmittel duscht.
Antworten