Erklärbar oder Namensraum-Bug?

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
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

Hallo,

gibt es dafür eine Erklärung:

Code: Alles auswählen

>>> foo = []
>>> def ok():
...     print(id(foo))
...     foo.append(1)
... 
>>> def strange():
...     print(id(foo))
...     foo.append(2)
...     foo += [2]
... 
>>> id(foo)
36658712
>>> ok()
36658712
>>> foo
[1]
>>> strange()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in strange
UnboundLocalError: local variable 'foo' referenced before assignment
mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
BlackJack

@mutetella: Erklärbar. Die Erklärung steht doch im Grunde in der Ausnahme.
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

Erklärbar??

Was genau macht den 'strange()' anderes als 'ok()'? Wenn die Ausnahme bei 'foo += [2]' auftreten würde..., wobei auch das eigenartig wäre... wenn da 'foo = foo + [2]' stünde, könnte ich mir das noch dadurch erklären, dass 'foo' danach auf eine andere Liste verweist... dann allerdings dürfte die Ausnahme nicht schon beim Versuch, die id abzurufen, erscheinen... :K
Was will mir die Ausnahme denn sagen? 'foo' muss vor seiner Verwendung auf ein Objekt verweisen. Ok. Tut es doch auch. Wäre dem nicht so, müsste 'ok()' doch dieselbe Ausnahme werfen?

Warum also kann 'ok()' auf 'foo' zugreifen, wenn es 'append()' verwendet, 'strange()' aber nicht, wenn es '__iadd__()' verwendet?

mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
fsck
User
Beiträge: 5
Registriert: Dienstag 4. Januar 2011, 22:13

Ein Name muss bevor er referenziert werden kann, erst an etwas gebunden werden. Ein Name in einem inneren Block verdeckt den Namen im äußeren Block. Die Sichtbarkeit von Namen wird statisch festgelegt, noch vor der Ausführung eines Blockes. (Ein Block ist ein Modul, eine Funktion, eine Klasse)

Du bindest im Block der Funktion 'strange()' den Namen 'foo', welcher den äußeren Namen verdeckt, an 'foo + [2]'. Das machst du erst nach 'print(id(foo))' , also kannst du den Namen nicht schon dort verwenden. Außerdem kann 'foo = foo + [2]' nicht funktioneren, da du den Namen 'foo' ja gerade bindest.
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

mutetella hat geschrieben:Warum also kann 'ok()' auf 'foo' zugreifen, wenn es 'append()' verwendet, 'strange()' aber nicht, wenn es '__iadd__()' verwendet?
Weil du nirgends __iadd__ verwendest. += ist nicht äquivalent zu __iadd__. += ist ein Operator der __iadd__ aufrufen kann – sofern es existiert – und vor allem ist es eine echte Zuweisung. Die Konsequenzen hat fsck schon erklärt.
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

@fsck:
Das ist mir soweit schon klar, nur: Weshalb kann dann 'ok()' auf 'foo', das außerhalb an eine Liste gebunden wurde, zugreifen?
Wenn ich 'ok()' aufrufe, existiert 'foo'. Das 'foo' aus dem äußeren Namensraum, ein anderes 'foo' gibt es ja nicht.
Wenn ich 'strange()' aufrufe, erscheint die Ausnahme, bevor ich überhaupt ein "inner-strange()"-'foo' erstelle. Und selbst wenn ich das täte, würde ich halt auf dieses 'foo' zugreifen.
Aber warum die Ausnahme an der Stelle, an der 'ok()' doch auch ohne Probleme mit 'foo' umgehen kann? Warum überhaupt eine Ausnahme? Irgendein 'foo' existiert doch allemal?

Viele Module arbeiten doch mit Konstanten, die auf Modulebene erstellt und gebunden werden und dann auch innerhalb von Funktionen ihre Gültigkeit haben. Eben das, was auch innerhalb von 'ok()' geschieht?

Wo ist die Kurve, die ich übersehen hab'?

mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

mutetella hat geschrieben:Wenn ich 'strange()' aufrufe, erscheint die Ausnahme, bevor ich überhaupt ein "inner-strange()"-'foo' erstelle. Und selbst wenn ich das täte, würde ich halt auf dieses 'foo' zugreifen.
Richtig, weil der Gültigkeitsbereich schon *vor der Ausführung des Blocks* feststeht. Und im Fall von ``strange`` ist nicht klar was ``foo`` nun ist. Ist es ein lokaler Name? Ist es ein Globaler? Im Fall von Python3: ist es ein nicht-Lokaler?
mutetella hat geschrieben:Viele Module arbeiten doch mit Konstanten, die auf Modulebene erstellt und gebunden werden und dann auch innerhalb von Funktionen ihre Gültigkeit haben. Eben das, was auch innerhalb von 'ok()' geschieht?

Wo ist die Kurve, die ich übersehen hab'?
Konstanten werden, nun, nicht aus den Funktionen zugewiesen, sonst wärens ja keine Konstanten, daher ist innerhalb der Funktionen klar das Namen die nicht im lokalen Namensraum sind, aus dem umgebenden, im Fall von ``ok`` also globalen Namensraum, stammen.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
deets

mutetella hat geschrieben: Wo ist die Kurve, die ich übersehen hab'?
Du kennst halt die scoping-Regeln von Python nicht. Python hat keine explizite Variablen-Deklaration, wie sie Sprachen wie zB Javascript oder auch Perl kennen.

Als Konsequenz daraus geht Python 2 zur Bestimmung des Scopes eines Namens in der statischen Analyse des Codes beim ersten ausfuehren so vor:

- ein Name auf modul-Ebene ist natuerlich (modul)-global
- ein Name in einer Funktion, der auf der linken Seite einer *zuweisung* auftaucht, ist Funktions-lokal. Also zb sowas hier:

Code: Alles auswählen

def foo():
     bar = "baz"
- ein Name, der hinter dem global-statement auftaucht ist global

Und daher kommt auch dein "Problem". In strange taucht foo als linker Teil einer Zuweisung auf (implizit, durch das +=), und damit macht Python das bereits zur Syntaxanalyse zu einem Funktions-lokalen Namen. Womit es dann auch gleich meckern kann, dass du den schon vor seiner Einfuehrung benutzt.

Dein ok() funktioniert, weil foo dort nichts zugewiesen wird, sondern das davon referenzierte Objekt *modifiziert*.
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

Vielen Dank für Eure guten Erklärungen... damit hab's dann auch ich kapiert :) !

Dass Python bereits vor der Ausführung einer Funktion die Synthax prüft, wusste ich nicht.


Was würdet ihr mir zur Lösung der beiden folgenden Probleme raten:

Um ein erweitertes Eingabefeld, quasi ein 'ext_input()', zu realisieren, gehe ich wie folgt vor:

Code: Alles auswählen

  ___
| stdin: '' -> warte
|    Tastatureingabe: 'a'
| ___
  ___
|stdin: 'a' -> übersetze Zeichen
|    lese 'a', übersetze als 'a', zeige 'a' an
|    sende '\033[?6n'
| __
  ___
|stdin: '\x1b[?20;1R' -> übersetze Zeichen
|    lese '\x1b' bis 'R', übersetze als (20, 1)
| __
  ___
|stdin: '' -> warte
|    Eingabe aus Zwischenspeicher: 'wort'
| __
  ___
|stdin: 'wort\x1b[?20;2R' -> übersetze Zeichen
|    lese 'w', übersetze als 'w', zeige 'w' an
|    sende '\033[?6n'
| __
  ___
|stdin: 'ort\x1b[?20;2R\x1b[?20;3R' -> übersetze Zeichen
|    lese 'o', übersetze als 'o', zeige 'o' an
|    sende '\033[?6n'
| __
  ___
|stdin: 'rt\x1b[?20;2R\x1b[?20;3R\x1b[?20;4R' -> übersetze Zeichen
|    lese 'r', übersetze als 'r', zeige 'r' an
|    sende '\033[?6n'
| __
...
Nachdem also etwas aus dem Zwischenspeicher eingefügt wird (oder auch dann, wenn Tasten sehr, sehr schnell hintereinander eingetippt(-trommelt) werden) erhält man erst wieder einen xterm-Report, wenn die Zeichen zuvor abgearbeitet sind.
Mein Ansatz:
  • Alle Zeichen bis zum xterm-Report auslesen, den Report auswerten und die zwischengespeicherten Zeichen dann wie 'stdin' behandeln und danach wieder auf 'stdin' wechseln.
    Mit anderen Worten: Zwischen jedes anzeigbare Zeichen einen xterm-Report einfügen.
  • Auf die ständige (teure) xterm-Abfrage verzichten, die Koordinaten einmal abfragen und intern aktuell halten.
Wie auch immer ich mich entscheide, ich muss entweder den Zwischenspeicher oder die Koordinaten auf Modulebene ablegen.
Folgende Möglichkeiten fallen mir ein:
  • Ich verzichte auf Zuweisungen innerhalb der Funktionen, die Zwischenspeicher und/oder Koordinaten aktualisieren.
  • Ich verwende ein dictionary.
  • Ich verwende zum Zwischenspeichern ein "echtes" file.
Ich denke mal, die Cursorposition einmalig abzufragen, intern aktuell zu halten und dafür ein dictionary zu verwenden ist am sinnvollsten.

Was meint ihr?

mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

mutetella hat geschrieben:Was meint ihr?
Ich meine dass das eine komplett andere Frage ist und einen eigenen Thread verdient :p
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

Ok, da hast Du Recht...

Für mein Cursorthema habe ich also einen neuen Thread geöffnet.
mutetella hat geschrieben:Wie auch immer ich mich entscheide, ich muss entweder den Zwischenspeicher oder die Koordinaten auf Modulebene ablegen.
Folgende Möglichkeiten fallen mir ein:

* Ich verzichte auf Zuweisungen innerhalb der Funktionen, die Zwischenspeicher und/oder Koordinaten aktualisieren.
* Ich verwende ein dictionary.
* Ich verwende zum Zwischenspeichern ein "echtes" file.
Das würde mich dennoch interessieren...
Wie könnte ich solche Informationen ohne Verwendung einer Klasse oder dem mühsamen weiterreichen von Funktion zu Funktion ablegen?

mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
Antworten