Jaka jest różnica między globals (), locals () i vars ()?

146

Jaka jest różnica między globals(), locals()i vars()? Co oni zwracają? Czy aktualizacje wyników są przydatne?

Ethan Furman
źródło
czy można zaktualizować którekolwiek z nich w Pythonie 3 i sprawić, by skrypt faktycznie działał?
Charlie Parker

Odpowiedzi:

172

Każdy z nich zwraca słownik:

  • globals() zawsze zwraca słownik przestrzeni nazw modułu
  • locals() Zawsze wraca do słownika bieżącej przestrzeni nazw
  • vars()powraca albo słownika bieżącej przestrzeni nazw (jeśli wywołana bez argumentów) lub w słowniku argumentu.

localsi varsprzydałoby się więcej wyjaśnień. Jeśli locals()jest wywoływana wewnątrz funkcji, aktualizuje dict o wartości bieżącej przestrzeni nazw zmiennej lokalnej (plus wszelkie zmienne zamykające) na ten moment i zwraca ją. Wielokrotne wywołania locals()w tej samej ramce stosu za każdym razem zwracają ten sam dykt - jest on dołączony do obiektu ramki stosu jako jego f_localsatrybut. Zawartość dyktu jest aktualizowana przy każdym locals()wywołaniu i każdym f_localsdostępie do atrybutu, ale tylko przy takich wywołaniach lub dostępach do atrybutów. Nie aktualizuje się automatycznie po przypisaniu zmiennych, a przypisanie wpisów w dict nie spowoduje przypisania odpowiednich zmiennych lokalnych:

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()

daje nam:

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

Pierwsza print(l)pokazuje tylko 'x'wpis, ponieważ przypisanie do lnastępuje po locals()wywołaniu. Drugi print(l), po locals()ponownym wywołaniu , pokazuje lwpis, mimo że nie zapisaliśmy wartości zwracanej. Trzeci i czwarty printpokazują, że przypisywanie zmiennych nie aktualizuje się li odwrotnie, ale po uzyskaniu dostępu f_localszmienne lokalne są locals()ponownie kopiowane do .

Dwie uwagi:

  1. To zachowanie jest specyficzne dla CPythona - inne Pythony mogą zezwolić aktualizacjom na automatyczne przywrócenie lokalnej przestrzeni nazw.
  2. W CPythonie 2.x można to zrobić, wstawiając exec "pass"linię do funkcji. Powoduje to przełączenie funkcji do starszego, wolniejszego trybu wykonywania, w którym locals()dict jest kanoniczną reprezentacją zmiennych lokalnych.

Jeśli locals()jest wywoływana poza funkcją, zwraca rzeczywisty słownik, który jest bieżącą przestrzenią nazw. Dalsze zmiany w przestrzeni nazw odzwierciedlane w słowniku, a zmiany w słowniku odzwierciedlane w przestrzeni nazw:

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

daje nam:

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

Jak dotąd wszystko, o czym powiedziałem, locals()odnosi się również do vars()... oto różnica: vars()przyjmuje pojedynczy obiekt jako swój argument, a jeśli dasz mu obiekt, zwraca wartość __dict__tego obiektu. W przypadku typowego obiektu __dict__jest to miejsce, w którym przechowywana jest większość danych atrybutów. Obejmuje to zmienne klas i globalne modułów:

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

co daje nam:

three

Zauważ, że funkcja __dict__jest przestrzenią nazw atrybutu, a nie zmiennymi lokalnymi. __dict__Przechowywanie zmiennych lokalnych przez funkcję nie miałoby sensu , ponieważ rekurencja i wielowątkowość oznaczają, że w tym samym czasie może istnieć wiele wywołań funkcji, każde z własnymi lokalnymi:

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)

co daje nam:

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

Tutaj fwywołuje się rekurencyjnie, więc wewnętrzne i zewnętrzne wywołania nakładają się. Każdy z nich widzi swoje własne zmienne lokalne podczas wywołania locals(), ale oba wywołania widzą to samof.__dict__ i f.__dict__nie mają żadnych zmiennych lokalnych.

Ethan Furman
źródło
4
Część „i wszelkie przypisania do słownika nie są odzwierciedlane w rzeczywistej lokalnej przestrzeni nazw” mogą być sformułowane nieco zbyt jasno .
Sven Marnach
Co dziwne, jeśli używasz, możesz uzyskać dostęp do zmiennych dodanych do słownika vars()lub locals()wywoływanego w funkcji eval(). EG: wyświetla def test(): huh = locals(); huh['d'] = 4; print eval('d')4 po test()wykonaniu!
Mark Mikofski
1
W rzeczywistości przypisanie do dict(zwrócone przez locals()) jest odzwierciedlane w lokalnej przestrzeni nazw, a zmiany w lokalnej przestrzeni nazw są odzwierciedlane w dict(w moim Pythonie). Tyle tylko, że specyfikacja nie gwarantuje takiego zachowania.
jazda na nartach
Użycie zakresu nazw terminów wygląda dla mnie łatwiej niż przestrzeń nazw .
przewyższenie
1
@overexchange: import thisoraz w Googlesite:docs.python.org namespace
Ethan Furman