Was ist der Unterschied zwischen Globals (), Locals () und Vars ()?

146

Was ist der Unterschied zwischen globals(), locals()und vars()? Was geben sie zurück? Sind Aktualisierungen der Ergebnisse hilfreich?

Ethan Furman
quelle
Kann man eines davon in Python 3 aktualisieren und das Skript tatsächlich funktionieren lassen?
Charlie Parker

Antworten:

172

Jedes von diesen gibt ein Wörterbuch zurück:

  • globals() Gibt immer das Wörterbuch des Modul- Namespace zurück
  • locals() Gibt immer ein Wörterbuch des aktuellen Namespace zurück
  • vars()Gibt entweder ein Wörterbuch des aktuellen Namespace (falls ohne Argument aufgerufen) oder das Wörterbuch des Arguments zurück.

localsund varskönnte etwas mehr Erklärung gebrauchen. Wenn locals()es innerhalb einer Funktion aufgerufen wird, aktualisiert es ein Diktat mit den Werten des aktuellen lokalen Variablennamensraums (plus aller Abschlussvariablen) ab diesem Moment und gibt es zurück. Mehrere Aufrufe an locals()denselben Stapelrahmen geben jedes Mal dasselbe Diktat zurück - es wird als f_localsAttribut an das Stapelrahmenobjekt angehängt . Der Inhalt des Diktats wird bei jedem locals()Aufruf und jedem f_localsAttributzugriff aktualisiert , jedoch nur bei solchen Aufrufen oder Attributzugriffen. Es wird nicht automatisch aktualisiert, wenn Variablen zugewiesen werden, und das Zuweisen von Einträgen im Diktat weist nicht die entsprechenden lokalen Variablen zu:

import inspect

def f():
    x = 1
    l = locals()
    print(l)
    locals()
    print(l)
    x = 2
    print(x, l['x'])
    l['x'] = 3
    print(x, l['x'])
    inspect.currentframe().f_locals
    print(x, l['x'])

f()

gibt uns:

{'x': 1}
{'x': 1, 'l': {...}}
2 1
2 3
2 2

Der erste print(l)zeigt nur einen 'x'Eintrag, da die Zuordnung lnach dem locals()Anruf erfolgt. Die zweite print(l)zeigt nach erneutem Aufruf locals()einen lEintrag, obwohl wir den Rückgabewert nicht gespeichert haben. Das dritte und vierte prints zeigen, dass das Zuweisen von Variablen nicht aktualisiert wird lund umgekehrt, aber nach dem Zugriff f_localswerden lokale Variablen locals()erneut kopiert .

Zwei Anmerkungen:

  1. Dieses Verhalten ist CPython-spezifisch. Bei anderen Pythons können die Aktualisierungen möglicherweise automatisch zum lokalen Namespace zurückkehren.
  2. In CPython 2.x ist es möglich, dass dies funktioniert, indem eine exec "pass"Zeile in die Funktion eingefügt wird. Dies schaltet die Funktion in einen älteren, langsameren Ausführungsmodus um, der das locals()Diktat als kanonische Darstellung lokaler Variablen verwendet.

Wenn locals()es außerhalb einer Funktion aufgerufen wird , gibt es das tatsächliche Wörterbuch zurück, das der aktuelle Namespace ist. Weitere Änderungen im Namensraum sind im Wörterbuch reflektiert und Änderungen im Wörterbuch sind im Namensraum wider:

class Test(object):
    a = 'one'
    b = 'two'
    huh = locals()
    c = 'three'
    huh['d'] = 'four'
    print huh

gibt uns:

{
  'a': 'one',
  'b': 'two',
  'c': 'three',
  'd': 'four',
  'huh': {...},
  '__module__': '__main__',
}

Bisher gilt alles, worüber ich gesagt habe, locals()auch für vars()... hier ist der Unterschied: vars()Akzeptiert ein einzelnes Objekt als Argument, und wenn Sie ihm ein Objekt geben, gibt es __dict__das Objekt zurück. Bei einem typischen Objekt werden __dict__hier die meisten Attributdaten gespeichert. Dies umfasst Klassenvariablen und Modulglobale:

class Test(object):
    a = 'one'
    b = 'two'
    def frobber(self):
        print self.c
t = Test()
huh = vars(t)
huh['c'] = 'three'
t.frobber()

was uns gibt:

three

Beachten Sie, dass eine Funktion __dict__ihr Attribut-Namespace ist, keine lokalen Variablen. Es wäre für eine Funktion nicht sinnvoll __dict__, lokale Variablen zu speichern, da Rekursion und Multithreading bedeuten, dass mehrere Funktionen gleichzeitig aufgerufen werden können, wobei jeder seine eigenen Ortsansässigen hat:

def f(outer):
    if outer:
        f(False)
        print('Outer call locals:', locals())
        print('f.__dict__:', f.__dict__)
    else:
        print('Inner call locals:', locals())
        print('f.__dict__:', f.__dict__)

f.x = 3

f(True)

was uns gibt:

Inner call locals: {'outer': False}
f.__dict__: {'x': 3}
Outer call locals: {'outer': True}
f.__dict__: {'x': 3}

Hier fruft sich selbst rekursiv, so dass die inneren und äußeren Anrufe überlappen. Jeder sieht seine eigenen lokalen Variablen, wenn er aufruft locals(), aber beide Aufrufe sehen dasselbe f.__dict__und enthalten f.__dict__keine lokalen Variablen.

Ethan Furman
quelle
4
Der Teil "und alle Zuweisungen zum Wörterbuch spiegeln sich nicht im tatsächlichen lokalen Namespace wider" ist möglicherweise etwas zu genau formuliert .
Sven Marnach
Seltsamerweise können Sie auf Variablen zugreifen, die einem vars()oder einem locals()Wörterbuch hinzugefügt wurden, das innerhalb einer Funktion aufgerufen wird, wenn Sie diese verwenden eval(). EG: def test(): huh = locals(); huh['d'] = 4; print eval('d')druckt 4, wenn test()ausgeführt wird!
Mark Mikofski
1
Die tatsächliche Zuordnung zum dict(zurückgegeben von locals()) spiegelt sich zufällig im lokalen Namespace wider, und Änderungen am lokalen Namespace spiegeln sich zufällig im dict(in meinem Python) wider . Das einzige ist, dass die Spezifikation dieses Verhalten nicht garantiert.
Skyking
Begriff Name Umfang Nutzung sieht für mich leichter , als Namespace .
Überaustausch
1
@overexchange: import thisund in Googlesite:docs.python.org namespace
Ethan Furman