2009-09-23 17 views

Odpowiedz

27

Kwestia ta jest nieco omówione w Python3 bug list. Ostatecznie, aby uzyskać ten problem, trzeba zrobić:

def foo(): 
    ldict = locals() 
    exec("a=3",globals(),ldict) 
    a = ldict['a'] 
    print(a) 

A jeśli sprawdzić the Python3 documentation on exec, zobaczysz następującą notatkę:

The default locals act as described for function locals() below: modifications to the default locals dictionary should not be attempted. Pass an explicit locals dictionary if you need to see effects of the code on locals after function exec() returns.

Wracając do a specific message on the bug report, Georg Brandl mówi:

To modify the locals of a function on the fly is not possible without several consequences: normally, function locals are not stored in a dictionary, but an array, whose indices are determined at compile time from the known locales. This collides at least with new locals added by exec. The old exec statement circumvented this, because the compiler knew that if an exec without globals/locals args occurred in a function, that namespace would be "unoptimized", i.e. not using the locals array. Since exec() is now a normal function, the compiler does not know what "exec" may be bound to, and therefore can not treat is specially.

Podkreślenie jest moje.

W związku z tym Python3 może lepiej zoptymalizować użycie zmiennych lokalnych przez , a nie, zezwalając na to zachowanie domyślnie.

I przez wzgląd na kompletność, jak wspomniano w powyższych uwag, to robi działa zgodnie z oczekiwaniami w Pythona 2.X:

Python 2.6.2 (release26-maint, Apr 19 2009, 01:56:41) 
[GCC 4.3.3] on linux2 
Type "help", "copyright", "credits" or "license" for more information. 
>>> def f(): 
...  a = 1 
...  exec "a=3" 
...  print a 
... 
>>> f() 
3 
+0

Rozumiem, jest to problem z locals(), który został zhackowany z exec w python 2.X. Ta kwestia nie jest tak jasno udokumentowana, jak bym chciała. Exec/locals zmieniające się z 2.X na 3.X powinny być wskazane gdzieś http://docs.python.org/3.1/library/functions.html#exec i myślę, że exec powinien mieć parametr wygody, który ominie tę optymalizację. .. – ubershmekel

+0

@MarkRushakoff Otrzymuję błąd z twoją implementacją w linii exec: TypeError: 'Dict' obiekt nie jest wywoływalny – Leo

+0

@Leo nie powinno to być 'ldict', nie' dyktować'? W każdym razie, nie pracuję już w Pythonie, więc jeśli to nie jest to, mam nadzieję, że ktoś inny zadzwoni. –

0

powodem, dla którego nie można zmienić zmienne lokalne wewnątrz funkcji przy użyciu exec w ten sposób i dlaczego exec akt droga robi można podsumować w następujący sposób:

  1. exec to funkcja, która dzieli swój lokalny scape z zakresu najbardziej wewnętrznej zakresie, w jakim to się nazywa.
  2. Za każdym razem, gdy zdefiniujesz nowy obiekt w zasięgu funkcji, będzie on dostępny w lokalnej przestrzeni nazw, tzn. Zmodyfikuje słownik local(). Podczas definiowania nowego obiektu w exec co robi to z grubsza odpowiednik następujący:

from copy import copy 
class exec_type: 
    def __init__(self, *args, **kwargs): 
     # default initializations 
     # ... 
     self.temp = copy(locals()) 

    def __setitem__(self, key, value): 
     if var not in locals(): 
      set_local(key, value) 
     self.temp[key] = value 

temp to tymczasowa przestrzeń nazw, które resetuje po każdej instancji. (Za każdym razem wywołać exec)


  1. Python zaczyna patrząc na nazwiska z lokalnej przestrzeni nazw. Jest znany jako sposób LEGB. Python rozpoczyna się od Local namespce, następnie analizuje zakresy Enclosing, następnie Global i na końcu wyszukuje nazwy w przestrzeni nazw Buit-in.

Bardziej kompleksowe Przykładem może my coś jak następuje:

g_var = 5 

def test(): 
    l_var = 10 
    print(locals()) 
    exec("print(locals())") 
    exec("g_var = 222") 
    exec("l_var = 111") 
    exec("print(locals())") 

    exec("l_var = 111; print(locals())") 

    exec("print(locals())") 
    print(locals()) 
    def inner(): 
     exec("print(locals())") 
     exec("inner_var = 100") 
     exec("print(locals())") 
     exec("print([i for i in globals() if '__' not in i])") 

    print("Inner function: ") 
    inner() 
    print("-------" * 3) 
    return (g_var, l_var) 

print(test()) 
exec("print(g_var)") 

wyjściowa:

{'l_var': 10} 
{'l_var': 10} 
# locals are the same 
{'l_var': 10, 'g_var': 222} 
# after adding g_var and changing the l_var it only adds g_var and left the l_var unchanged 
{'l_var': 111, 'g_var': 222} 
# l_var is changed because we are changing and printing the locals in one instantiation (one call to exec) 
{'l_var': 10, 'g_var': 222} 
{'l_var': 10, 'g_var': 222} 
# In both function's locals and exec's local l_var is unchanged and g_var is added 
Inner function: 
{} 
{'inner_var': 100} 
{'inner_var': 100} 
# inner_function's local is same as exec's local 
['g_var', 'test'] 
# global is only contain g_var and function name (after excluding the special methods) 
--------------------- 

(5, 10) 
5