Quelle est la différence entre globals (), locaux () et vars ()?

146

Quelle est la différence entre globals(), locals()et vars()? Que reviennent-ils? Les mises à jour des résultats sont-elles utiles?

Ethan Furman
la source
peut-on mettre à jour l'un de ceux-ci en python 3 et faire fonctionner le script?
Charlie Parker

Réponses:

172

Chacun de ceux-ci renvoie un dictionnaire:

  • globals() renvoie toujours le dictionnaire de l' espace de noms du module
  • locals() renvoie toujours un dictionnaire de l' espace de noms courant
  • vars()renvoie soit un dictionnaire de l'espace de noms courant (s'il est appelé sans argument) soit le dictionnaire de l'argument.

localset varspourrait utiliser quelques explications supplémentaires. Si locals()est appelé dans une fonction, il met à jour un dict avec les valeurs de l'espace de nom de la variable locale actuelle (plus toutes les variables de fermeture) à partir de ce moment et le renvoie. Plusieurs appels à locals()dans le même cadre de pile renvoient le même dict à chaque fois - il est attaché à l'objet de cadre de pile en tant f_localsqu'attribut. Le contenu du dict est mis à jour à chaque locals()appel et à chaque f_localsaccès d'attribut, mais uniquement sur de tels appels ou accès d'attribut. Il ne se met pas à jour automatiquement lorsque des variables sont affectées et l'affectation d'entrées dans le dict n'affectera pas les variables locales correspondantes:

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

nous donne:

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

Le premier print(l)n'affiche qu'une 'x'entrée, car l'affectation à lse produit après l' locals()appel. Le second print(l), après avoir locals()rappelé, affiche une lentrée, même si nous n'avons pas enregistré la valeur de retour. Les troisième et quatrième prints montrent que l'affectation de variables ne se met pas à jour let vice versa, mais après que nous y accédons f_locals, les variables locales sont à locals()nouveau copiées .

Deux notes:

  1. Ce comportement est spécifique à CPython - d'autres Pythons peuvent autoriser les mises à jour à revenir automatiquement à l'espace de noms local.
  2. Dans CPython 2.x, il est possible de faire ce travail en mettant une exec "pass"ligne dans la fonction. Cela fait passer la fonction à un mode d'exécution plus ancien et plus lent qui utilise le locals()dict comme représentation canonique des variables locales.

Si locals()est appelé en dehors d' une fonction, il renvoie le dictionnaire réel qui est l'espace de noms actuel. D'autres modifications apportées à l'espace de noms sont reflétées dans le dictionnaire et les modifications apportées au dictionnaire sont reflétées dans l'espace de noms:

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

nous donne:

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

Jusqu'à présent, tout ce que j'ai dit locals()est également vrai pour vars()... voici la différence: vars()accepte un seul objet comme argument, et si vous lui donnez un objet, il renvoie le __dict__de cet objet. Pour un objet typique, __dict__c'est là que la plupart de ses données d'attributs sont stockées. Cela inclut les variables de classe et les globaux de module:

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

ce qui nous donne:

three

Notez qu'une fonction __dict__est son espace de noms d'attribut, pas des variables locales. Cela n'aurait aucun sens pour une fonction de __dict__stocker des variables locales, car la récursivité et le multithreading signifient qu'il peut y avoir plusieurs appels à une fonction en même temps, chacun avec ses propres locals:

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)

ce qui nous donne:

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

Ici, fs'appelle lui-même récursivement, de sorte que les appels internes et externes se chevauchent. Chacun voit ses propres variables locales lorsqu'il appelle locals(), mais les deux appels voient la même chose f.__dict__et f.__dict__ne contiennent aucune variable locale.

Ethan Furman
la source
4
La partie "et toutes les affectations au dictionnaire ne sont pas reflétées dans l'espace de noms local réel" pourrait être rédigée un peu à définir .
Sven Marnach
Curieusement, vous pouvez accéder aux variables ajoutées à un dictionnaire vars()ou locals()appelé dans une fonction si vous utilisez eval(). EG: def test(): huh = locals(); huh['d'] = 4; print eval('d')imprime 4 quand test()est exécuté!
Mark Mikofski
1
En fait, l'affectation au dict(retourné par locals()) se trouve être reflétée dans l'espace de noms local et les modifications apportées à l'espace de noms local se trouvent être reflétées dans le dict(dans mon python). La seule chose est que la spécification ne garantit pas ce comportement.
skyking
L' utilisation de la portée des noms de termes me semble plus facile que l' espace de noms .
surexchange
1
@overexchange: import thiset dans googlesite:docs.python.org namespace
Ethan Furman