foo.__dic__ = ??

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
Benutzeravatar
Goswin
User
Beiträge: 363
Registriert: Freitag 8. Dezember 2006, 11:47
Wohnort: Ulm-Böfingen
Kontaktdaten:

Wenn ich eine lokale Variable in einer Funktion definiere, warum befindet sich diese nicht im __dict__ der Funktion? Wie kann ich von außen auf diese Variable zugreifen? (black magic needed)

Code: Alles auswählen

>>> def foo(): a=2
... 
>>> foo.__dict__
{}
im Gegensatz zu

Code: Alles auswählen

>>> def foo(): pass
... 
>>> foo.a=2
>>> foo.__dict__
{'a': 2}
Benutzeravatar
helduel
User
Beiträge: 300
Registriert: Montag 23. Juli 2007, 14:05
Wohnort: Laupheim

Moin,

wieso erwartest du das? Der Body einer Funktion wird genau dann ausgeführt, wenn die Funktion aufgerufen wird. Und die lokalen Variablen sind auch nur dann vorhanden. Wenn die Funktion zurückkehrt, ist das ganze wieder weg (Closures mal ausgenommen). Inwiefern soll es also Sinn machen, auf diese lokalen Variablen zuzugreifen? Was willst du denn erreichen?

Gruß,
Manuel
lunar

@Goswin: Man kann von außen überhaupt nicht auf lokale Variablen zugreifen, da diese überhaupt nur während einer Ausführung dieser Funktion existieren, und zwar separat für jeden Aufruf (e.g. bei Rekursionen oder Aufrufen in verschiedenen Threads). Die lokale Variable "a" gibt es also per se gar nicht, es gibt nur verschiedene Bindungen namens "a" in verschiedenen lokalen Namensräumen in verschiedenen Aufrufen dieser Funktion.

"foo.__dict__" hat mit dem lokalen Namensraum der Funktion überhaupt nichts zu tun. Es enthält vielmehr Attribute, die auf dem Funktionsobjekt selbst gesetzt werden:

Code: Alles auswählen

>>> def foo():
...     a = 42
...     return a
... 
>>> foo.__dict__
{}
>>> foo.bar = 'Hello World'
>>> foo.__dict__
{'bar': 'Hello World'}
>>> foo.bar
'Hello World'
Deine Erwartung offenbart ein größeres Missverständnis der Semantik von Python, insbesondere derjenigen der Bindungen und Namensräume.
Benutzeravatar
Goswin
User
Beiträge: 363
Registriert: Freitag 8. Dezember 2006, 11:47
Wohnort: Ulm-Böfingen
Kontaktdaten:

@Heiduel:
Was ich erreichen möchte, ist die Feinheiten der Funktionsweise von Python zu verstehen.

Deine Aussage "Wenn die Funktion zurückkehrt, ist das ganze WIEDER weg" [meine Hervorhebung] stimmt wohl nicht ganz. Wenn ich nämlich vorgehe wie in

Code: Alles auswählen

def foo():
   a=2
   print(foo.__dict__)
foo()
also auf foo.__dict__ zugreife BEVOR ich die Funktion verlasse, dann erhalte ich ebenfalls "{}". Sollte foo.__dict__ vielleicht nur die globals enthalten?

Mir schwebte etwas wie folgt vor:

Code: Alles auswählen

>>> def foo():
...    a=2
...    fuu()
... 
>>> def fuu():
...    print(foo.a)
... 
>>> foo()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in foo
  File "<stdin>", line 2, in fuu
AttributeError: 'function' object has no attribute 'a'
da geschieht der Zugriff WÄHREND die Funktion foo noch läuft.


@lunar: Ganz wie du es sagst, meine
Erwartung offenbart ein größeres Missverständnis der Semantik von Python, insbesondere derjenigen der Bindungen und Namensräume
Diese Art von Missverständnissen möchte ich gerne beseitigen.
Benutzeravatar
helduel
User
Beiträge: 300
Registriert: Montag 23. Juli 2007, 14:05
Wohnort: Laupheim

Goswin hat geschrieben:@Heiduel:
Was ich erreichen möchte, ist die Feinheiten der Funktionsweise von Python zu verstehen.

Deine Aussage "Wenn die Funktion zurückkehrt, ist das ganze WIEDER weg" [meine Hervorhebung] stimmt wohl nicht ganz. Wenn ich nämlich vorgehe wie in

Code: Alles auswählen

def foo():
   a=2
   print(foo.__dict__)
foo()
also auf foo.__dict__ zugreife BEVOR ich die Funktion verlasse, dann erhalte ich ebenfalls "{}". Sollte foo.__dict__ vielleicht nur die globals enthalten?
Inwiefern widerspricht das meiner Aussage? Lokale und globale Variablen innerhalb einer Funktion werden nie im __dict__ des Funktionsobjektes landen. a gibt es nur solange die Funktion ausgeführt wird. Wenn die Funktion zurückkehrt, ist a futsch.
Mir schwebte etwas wie folgt vor:

Code: Alles auswählen

>>> def foo():
...    a=2
...    fuu()
... 
>>> def fuu():
...    print(foo.a)
... 
>>> foo()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in foo
  File "<stdin>", line 2, in fuu
AttributeError: 'function' object has no attribute 'a'
da geschieht der Zugriff WÄHREND die Funktion foo noch läuft.
a ist eine lokale Variable, die nur innerhalt der Funktion gültig ist. Sie ist nicht an das Funktionsobject gebunden. Wenn du Zugriff auf die lokalen Variablen willst, dann könntest du mittels locals() die Variablen explizit übergeben:

Code: Alles auswählen

>>> def foo():
...    a=2
...    fuu(locals())
... 
>>> def fuu(foo_locals):
...    print(foo_locals['a'])
Gruß,
Manuel
lunar

@Goswin: Lies meinen Beitrag nochmal: "__dict__" hat nichts, aber auch wirklich rein gar nichts mit lokalen Namen zu tun. Lokale Namen sind niemals über das Funktionsobjekt erreichbar [1]. "__dict__" ist vom lokalen Namensraum einer Funktion vollkommen unabhängig. Als eigenständiges Objekt ist der lokale Namensraum einer Funktion nur über "locals()" zu erreichen, und das auch nur innerhalb der Funktion.

[1] Bevor jemand schreit: In CPython kommt man über "__code__" schon an lokale Namen heran, nur sollte man das nicht tun.
BlackJack

@lunar: Im Grunde kommt man an den lokalen Namensraum auch mit `locals()` nicht als Objekt heran. Da bekommt man nur eine Kopie als Wörterbuch, die aber keine Verbindung mehr mit dem tatsächlichen Namensraum hat. Wenn ich mal Korinthen kacken darf. :-)
lunar

@BlackJack: Darfst Du :) Ist ja auch nicht unwichtig, immerhin bedeutet es, dass man den lokalen Namensraum über "locals()" nicht ändern kann. Danke für die Korrektur.
Benutzeravatar
Defnull
User
Beiträge: 778
Registriert: Donnerstag 18. Juni 2009, 22:09
Wohnort: Göttingen
Kontaktdaten:

__dict__ enthält die Instanz-Attribute eines Objektes. Da Funktionen auch Objekte sind, kannst du ihnen Attribute zuweisen. Das hat aber nichts mit lokalen variablen zu tun:

Code: Alles auswählen

>>> def func(): pass
...
>>> func.a = 5
>>> func.__dict__
{'a': 5}
Der Code in einer Funktion kann auch auf diese Attribute zugreifen, auch wenn das eher ungewöhnlich ist:

Code: Alles auswählen

>>> def func():
...   func.a += 1
...
>>> func.a = 1
>>> func.a
1
>>> func()
>>> func.a
2
Wenn du so etwas versuchst, ist das allerdings ein deutliches Zeichen: Was du eigentlich willst, ist ein Objekt und keinen Funktion.
Bottle: Micro Web Framework + Development Blog
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

Vielleicht sollte auch mal erwähnt werden dass man auf __dict__ möglichst nicht zugreift, besonders wenn Speicherverbrauch/Performance eine Rolle spielt.
Benutzeravatar
Goswin
User
Beiträge: 363
Registriert: Freitag 8. Dezember 2006, 11:47
Wohnort: Ulm-Böfingen
Kontaktdaten:

lunar hat geschrieben: Immerhin bedeutet es, dass man den lokalen Namensraum über "locals()" nicht ändern kann.
Das ist für mich in Ordnung, ich will ja gar nichts ändern, sondern nur lesen.


Ich nehme mal Heiduels Rezept in leicht abgewandelter Form, nämlich mit

Code: Alles auswählen

def zeige(locs,expr):
   print( "{} = {}".format(expr.strip(),eval(expr,locs)) )

def foo():
   a=2
   zeige(locals(),'a+1')

foo()  #druckt "a+1 = 3"
Vielen Dank für eure Hilfe!
Benutzeravatar
Goswin
User
Beiträge: 363
Registriert: Freitag 8. Dezember 2006, 11:47
Wohnort: Ulm-Böfingen
Kontaktdaten:

NACHTRAG:

Ich habe nun gelernt, dass ich auf die locals() einer Funktion von außen nicht zugreifen kann, wenn mir diese locals() nicht explizit übergeben werden.

Welche praktische Anwendung folgt daraus? Meine Programme wimmeln nur so von internen Objektbezeichnern der Form _xyz, da sie andere Objekte nichts angehen. Wenn man aber auf gewisse lokale Variablen sowieso nicht herankommt, sind diese Unterstriche wahrscheinlich oft überflüssig, und ich könnte einfach nur xyz schreiben, was auch viel besser lesbar wäre.

Gibt es eine einfache Regel, nach der ich weiß, wann ich auf die Unterstriche verzichten kann und wann ich sie unbedingt setzen sollte?
Python nervt. (Nicht immer, und natürlich nur mich, niemals andere)
BlackJack

@Goswin: Das macht dann eigentlich nur Sinn bei Namen auf Modulebene, Methodennamen, und Attributnamen, die nicht zur öffentlichen Schnittstelle Deiner Objekte gehören.

Bei lokalen Namen nehme ich einen führenden Unterstrich in der Regel bei Namen denen ich etwas zuweise, sie dann aber nicht verwende. Als Dokumentation, dass das Absicht ist, dass sie nicht verwendet werden. Zum Beispiel wenn mich die API zwingt bei der Definition einer Rückruffunktion Argumente entgegen zu nehmen, die ich innerhalb der Funktion aber gar nicht verwende.
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Das Setzen eines Unterstrichs ist lediglich ein *Hinweis* darauf, dass das entsprende Attribut nicht zur öffentlichen Schnittstelle gehört. Es hindert den Programmierer jedoch nicht daran, trotzdem auf Attribute mit Unterstrichen zuzugreifen. Es macht IMHO auch wenig Sinn, "private" Attribute vollständig unsichtbar zu machen. Man hat den Anwender hinreichend gewarnt und er muss sich den möglichen Konsequenzen (plötzliches Verschwinden in einer neuen Version, unerwartete Veränderung von Verhalten, etc), wenn er sie benutzt, bewusst sein. Oft ist es nichtmal weiter tragisch, "öffentliche" Attribute zu haben. In meinen Programmen bleiben Unterstriche eher die Ausnahme, bei dir klingt es schon fast nach exzessivem Gebrauch von Unterstrichen...
Benutzeravatar
Goswin
User
Beiträge: 363
Registriert: Freitag 8. Dezember 2006, 11:47
Wohnort: Ulm-Böfingen
Kontaktdaten:

BlackJack hat geschrieben:[Namen die mit mit Unterstrich beginnen] macht dann eigentlich nur Sinn bei Namen auf Modulebene, Methodennamen, und Attributnamen, die nicht zur öffentlichen Schnittstelle Deiner Objekte gehören.
Wie ist folgende Empfehlung aus PEP_8 zu verstehen:
Benutze einen führenden Unterstrich nur für nicht-öffentliche Methoden und Instanzvariablen.
Sind hier statt "Instanzvariablen" vielleicht "Klassennamen" gemeint?
(Nicht-öffentliche Klassennamen sollten doch auch mit einem Unterstrich beginnen! Und ich denke, *alle* Variablen sind Instanzvariablen, da in Python *alle* Variablen auf Instanzen von irgendwelchen Objekten weisen, das ist ja das einzige, was eine Variable tun kann)
BlackJack

@Goswin: Nicht alle Variablen sind Instanzvariablen. Lokale Namen zum Beispiel nicht. Es geht ja nicht darum welcher Wert an den Namen gebunden ist — das muss immer ein Objekt sein — sondern ob und wo der Name als Attribut definiert ist. Da sind dann streng genommen alle Attribute auch „Instanz“-Attribute, aber bei Klassen ist es zum Beispiel sinnvoller von Klassenattributen zu reden, auch wenn eine Klasse natürlich auch ein Objekt, und damit eine „Instanz“ ist.

Klassennamen sind im Grunde in „Instanz“-Variablen enthalten, denn man kann das Modul in dem so eine Name definiert ist, ja als Singleton-Objekt auffassen.
Antworten