2009-09-20 6 views
11

locals est une fonction intégrée qui renvoie un dictionnaire de valeurs locales. La documentation dit:La modification des locales en Python

Avertissement

Le contenu de ce dictionnaire doit pas modifié; les modifications peuvent ne pas affecter les valeurs des variables locales utilisées par l'interpréteur.

Malheureusement, exec a le même problème dans Python 3.0. Y a-t-il un moyen de contourner cela?

cas d'utilisation

Tenir compte:

@depends("a", "b", "c", "d", "e", "f") 
def test(): 
    put_into_locals(test.dependencies) 

dépend stocke les chaînes fournies dans ses arguments dans une liste test.dependences. Ces chaînes sont des clés dans un dictionnaire d. Je voudrais être en mesure d'écrire put_into_locals afin que nous puissions tirer les valeurs de d et les mettre dans les locaux. Est-ce possible?

+0

Lien vers la documentation appropriée: https://docs.python.org/2/library/functions.html#locals –

+0

Pourquoi 'tester. dependencies = ["a", "b", "c", "d", "e", "f"] 'travailler, puis décorer l'affectation que j'ai écrit ci-dessus à votre fonction' test() '? –

+0

avez-vous réussi à mettre à jour/modifier les locaux ou non? –

Répondre

11

Je viens de tester exec et ça marche en Python 2.6.2

>>> def test(): 
...  exec "a = 5" 
...  print a 
... 
>>> test() 
5 

Si vous utilisez Python 3.x, il ne fonctionne plus parce que les habitants sont optimisés comme un tableau à l'exécution, au lieu d'utiliser un dictionnaire. Lorsque Python détecte "l'instruction exec", Python force le stockage local de tableau en dictionnaire. Cependant puisque "exec" est une fonction dans Python 3.x, le compilateur ne peut pas faire cette distinction puisque l'utilisateur aurait pu faire quelque chose comme "exec = 123".

http://bugs.python.org/issue4831

Pour modifier les habitants d'une fonction sur la volée est impossible sans plusieurs conséquences: normalement, locaux de fonction ne sont pas stockés dans un dictionnaire, mais un tableau, dont indices sont déterminés au moment de la compilation à partir des paramètres régionaux connus. Cela entre au moins avec les nouveaux locaux ajoutés par exec. L'ancienne instruction exec contournée, parce que le compilateur savait que si un exec sans GLOBALS/locals args a eu lieu dans une fonction , cet espace serait « unoptimized », à savoir ne pas utiliser le tableau de la population locale. Depuis exec() est maintenant une fonction normale , le compilateur ne sait pas ce que "exec" peut être lié, et ne peut donc pas traiter est spécialement.

+0

Je pense qu'il est assez concluant que ce n'est tout simplement pas possible – Casebash

+1

@Casebash, c'est probablement possible, il nécessite juste des octets de code hacks ou Python 2.x – Unknown

+0

D'accord, je vais laisser cette question non résolue pour l'instant – Casebash

-1

Je ne suis pas sûr si elle est soumise aux mêmes restrictions, mais vous pouvez obtenir une référence directe à la trame courante (et à partir de là, le dictionnaire des variables locales) à travers le module inspecter:

>>> import inspect 
>>> inspect.currentframe().f_locals['foo'] = 'bar' 
>>> dir() 
['__builtins__', '__doc__', '__name__', '__package__', 'foo', 'inspect'] 
>>> foo 
'bar' 
+7

C'est exactement la même chose que les locaux(); 'inspect.currentframe(). f_locals est local()' est vrai. –

+1

Ce n'est pas ** totalement ** faux, mais cela ne fonctionne que lorsque le cadre est le plus haut, c'est-à-dire la portée globale. Il ne fonctionnera pas dans les domaines locaux. – bendtherules

3

Ce n'est pas possible. Je pense que c'est pour permettre des optimisations de performance plus tard. Python bytecode référence les locals par index, pas par nom; Si l'on demandait à locals() d'être accessible en écriture, cela pourrait empêcher les interprètes de mettre en œuvre certaines optimisations ou de les rendre plus difficiles. Je suis à peu près certain que vous ne trouverez aucune API de base qui vous garantisse que vous pouvez éditer des locals comme ceci, car si cette API pouvait le faire, locals() n'aurait pas cette restriction non plus. N'oubliez pas que tous les locaux doivent exister au moment de la compilation; Si vous référencez un nom qui n'est pas lié à un local lors de la compilation, le compilateur suppose qu'il s'agit d'un global. Vous ne pouvez pas "créer" des locaux après la compilation.

Voir this question pour une solution possible, mais c'est un hack sérieux et vous ne voulez vraiment pas faire cela.

Notez qu'il ya un problème fondamental avec votre code d'exemple:

@depends("a", "b", "c", "d", "e", "f") 
def test(): 
    put_into_locals(test.dependencies) 

"test.dependencies" ne se réfère pas à « f.dependencies » où f est la fonction actuelle; c'est référencer le "test" de la valeur globale réelle. Cela signifie que si vous utilisez plus d'un décorateur:

@memoize 
@depends("a", "b", "c", "d", "e", "f") 
def test(): 
    put_into_locals(test.dependencies) 

il ne marchera plus, puisque « test » est memoize sa fonction Emballé, ne dépend de. Python vraiment besoin d'un moyen de se référer à "la fonction en cours d'exécution" (et class).

5

Les variables locales sont modifiées par des instructions d'affectation.

Si vous avez des clés de dictionnaire qui sont des chaînes, n'en faites pas non plus des variables locales - utilisez-les simplement comme des clés de dictionnaire.

Si absolument devez avoir des variables locales le font.

def aFunction(a, b, c, d, e, f): 
    # use a, b, c, d, e and f as local variables 

aFunction(**someDictWithKeys_a_b_c_d_e_f) 

Cela va remplir certaines variables locales de votre dictionnaire sans rien faire de magique.

+0

juste ce que je pensais; vous pouvez également créer dynamiquement une fonction; voir l'aide (types.FunctionType) – gatoatigrado

+4

Ceci est une idée intéressante. Cependant, il existe de nombreuses applications dans lesquelles le dictionnaire contient en réalité de nombreuses autres variables (qui ne sont pas nécessaires à 'aFunction()'), ce qui rend la définition actuelle de 'aFunction()' brisée. Une généralisation utile est: 'aFonction (a, b, c, d, e, f, ** kwargs)'. – EOL

+0

@EOL: Les variables de paramètre supplémentaires font-elles une rupture de fonction? C'est difficile à imaginer. Quelques variables supplémentaires devraient être - bien - juste des variables. Une fonction qui se casse en raison de quelques variables supplémentaires a un design remarquablement pauvre. Il vaudrait mieux fixer cette fonction. –

1

Je le stocker dans une variable:

refs = locals() 
def set_pets(): 
    global refs 
    animals = ('dog', 'cat', 'fish', 'fox', 'monkey') 
    for i in range(len(animals)): 
     refs['pet_0%s' % i] = animals[i] 

set_pets() 
refs['pet_05']='bird' 
print(pet_00, pet_02, pet_04, pet_01, pet_03, pet_05) 
>> dog fish monkey cat fox bird 

Et si vous voulez tester votre dict avant de le mettre dans les locaux():

def set_pets(): 
    global refs 
    sandbox = {} 
    animals = ('dog', 'cat', 'fish', 'fox', 'monkey') 
    for i in range(len(animals)): 
     sandbox['pet_0%s' % i] = animals[i] 
    # Test sandboxed dict here 
    refs.update(sandbox) 

Python 3.6.1 sur Mac OS Sierra

Questions connexes