2010-12-19 18 views
45

Wygląda na to, że nie można użyć exec w funkcji, która ma podfunkcje ...Dlaczego exec nie działa w funkcji z podfunkcją?

Ktoś wie, dlaczego ten kod Pythona nie działa? Wystąpił błąd w exec w teście2. Ponadto, wiem, że exec nie jest dobrym stylem, ale zaufaj mi, używam exec z odpowiedniego powodu. Nie użyłbym tego inaczej.

#!/usr/bin/env python 
# 

def test1(): 
    exec('print "hi from test1"') 

test1() 

def test2(): 
    """Test with a subfunction.""" 
    exec('print "hi from test2"') 
    def subfunction(): 
     return True 

test2() 

EDYCJA: Zawęziłem błąd do funkcji w podfunkcji. Nie ma nic wspólnego ze słowem kluczowym raise.

+0

Istnieje takie samo ograniczenie z 'import * z ...'. – osa

+0

Wygląda na to, że rozumowanie przez dict uważane jest za podfunkcje. – dbliss

Odpowiedz

58

Prawidłowo. Nie można użyć exec w funkcji, która ma podfunkcje, chyba że podasz kontekst. Od docs:

Jeśli Exec jest używana w funkcji, a funkcja zawiera zagnieżdżony blok z zmiennych wolnych, kompilator podnieść SyntaxError chyba że exec wyraźnie określa lokalną nazw dla Exec . (Innymi słowy „exec obj” byłoby nielegalne, ale „exec obj w ns” byłoby zgodne z prawem.)

Jest to dobry powód, dla którego to pewnie zrozumieć, gdyby nie była niedziela noc. Teraz następne pytanie: Dlaczego używasz exec? Jest bardzo rzadko potrzebny. Mówisz, że masz dobry powód. Czuję się do tego sceptycznie nastawiony. ;) Jeśli masz dobry powód, powiem ci obejście tego problemu. :-P

No cóż, tutaj jest to w każdym razie:

def test2(): 
    """Test with a subfunction.""" 
    exec 'print "hi from test2"' in globals(), locals() 
    def subfunction(): 
     return True 
+0

Przykładem jest implementacja API opartego na ludzkich i obiektowych funkcjach dla ezoterycznej bazy danych NoSQL, która ma jedynie bardzo niskie powiązania z Pythonem. Chociaż, dodatkowe pytanie dla Ciebie: czy istnieje sposób, aby wstrzyknąć "exec" - ed kodu do określonej klasy? Jeśli tak, w jaki sposób? – chiffa

+1

@AndreiKucharavy Nie, 'exec' wykonuje kod, więc nie ma czegoś takiego jak" exec "-ed kod do wstrzyknięcia. Kod Execed to kod wykonywany. Możesz skompilować kod i wstrzyknąć to, może to właśnie masz na myśli? Ale zwykle nie musisz go kompilować, po prostu wstrzykujesz. Nie ma żadnego oczywistego powodu, dla którego musisz wykonać komendę tylko dlatego, że potrzebujesz interfejsu API OO do bazy danych NoSQL. –

+0

Niestety, mój zły: nie wstrzykiwać kodu, ale wstaw go, więc można go wywoływać jako część konkretnej klasy. Chodzi o to, aby przeanalizować utworzony przez użytkownika plik konfiguracyjny iz definicji wygenerować rodzinę obiektów, które można wywoływać jako proste obiekty Pythona, ale pod maską implementuje interfejs buforujący z bazą danych, trochę jak SQLAlchemy, ale bez potrzeby napisać jawnie wszystkie odwzorowania i kod definicji obiektu. – chiffa

1

Błąd wydaje się być dość oczywiste dla mnie:

SyntaxError: unqualified exec is not allowed in function 'test2' it contains a nested function with free variables 

See PEP 227 o więcej informacji: http://www.python.org/dev/peps/pep-0227/

4

To działa dobrze w Pythonie 3.1.3, po zmodyfikowaniu oświadczenie druku do korzystania drukuj funkcjonować.

W Pythonie 2.6, generuje SyntaxError: unqualified exec is not allowed in function 'test2' it contains a nested function with free variables, nie sądzę, że to błąd.

24

Chociaż w Pythonie to wygląda trochę jak zmienne lokalne są przechowywane w słowniku locals(), zwykle nie są. Zamiast tego są one najczęściej przechowywane na stosie i dostępne przez indeks. Powoduje to, że wyszukiwanie zmiennych lokalnych jest szybsze, niż przy każdorazowym wyszukiwaniu słownika. Jeśli używasz funkcji locals(), otrzymujesz świeży słownik utworzony ze wszystkich zmiennych lokalnych i dlatego przypisywanie do locals() nie działa.

Istnieje kilka wyjątków od tego scenariusza:

Podczas korzystania niekwalifikowaną exec wewnątrz funkcji Python wyłącza optymalizację i wykorzystuje prawdziwe słownik dla zmiennych lokalnych. Oznacza to, że można tworzyć lub aktualizować zmienne z poziomu exec, ale oznacza to również, że wszystkie lokalne uprawnienia dostępu w tej funkcji będą działać wolniej.

Innym wyjątkiem jest to, że podczas zagnieżdżania funkcji funkcja wewnętrzna może uzyskać dostęp do zmiennych lokalnych w zewnętrznym zakresie funkcji.Kiedy to robi, zmienna jest przechowywana w obiekcie "komórkowym", a nie na stosie. Dodatkowy poziom dwukierunkowości spowalnia działanie wszystkich zmiennych o zakresie, bez względu na to, czy uzyskujesz dostęp do nich z wewnętrznej czy zewnętrznej funkcji.

Połowu, które napotkano, jest to, że te dwa wyjątki od sposobu przechowywania zmiennych lokalnych są niezgodne. Nie możesz mieć zmiennej zapisanej w słowniku i dostępnej poprzez odwołanie do komórki w tym samym czasie. Python 2.x naprawia to, uniemożliwiając wykonanie exec, nawet w takich przypadkach, w których nie próbujesz użyć żadnych zmiennych o zakresie.

2

Jest to dość ciekawy przypadek:

>>> def func(): 
...  exec('print "hi from func"') 
...  def subfunction(): 
...   return True 
... 
    File "<stdin>", line 2 
SyntaxError: unqualified exec is not allowed in function 'func' because 
it contains a nested function with free variables 

Powodem dlaczego to nie działa w rzeczywistości jest to, że subfunction zawiera zmienną wolną, a ponieważ w Pythonie 2, exec teoretycznie może zmodyfikować mieszkańców w zawierającym nie można by zdecydować, czy zmienna powinna być związana z zakresem funkcji globalnej czy nadrzędnej. Jeden z wersetów w Zen Pythona to: "W obliczu niejednoznaczności odrzuć pokusę odgadnięcia". i to właśnie robi Python 2.

Teraz pytanie brzmi: co to za zmienna niezwiązana? Cóż, to jest True!

Rzeczywiście jest powtarzalny z None:

>>> def func(): 
...  exec('print "hi from func"') 
...  def subfunction(): 
...   return None 
... 
    File "<stdin>", line 2 
SyntaxError: unqualified exec is not allowed in function 'test2' because it contains a nested 
function with free variables 

Nawet None nie można przypisać, i jest uważana za stałej w kodu bajtowego, parser buggy uważa, że ​​jest to zmienna niezwiązany tutaj.

Ale jeśli zastąpić go 1 i działa bez problemów:

>>> def test2(): 
...  exec('print "hi from func"') 
...  def subfunction(): 
...   return 1 
... 
>>> 

Aby uniknąć tego błędu, określa sposobu globalnych i ewentualnie mieszkańców że mają być stosowane przez exec, słownie:

>>> def test2(): 
...  exec 'print "hi from test2"' in {} 
...  def subfunction(): 
...   return None 
... 
>>> 

W języku Python 3, exec jest tylko prostą funkcją i nie jest obsługiwany specjalnie przez analizator składni lub kompilator kodu bajtowego. W języku Python 3 exec nie można ponownie powiązać nazw lokalnych funkcji, a tym samym nie występuje ten błąd składni i niejednoznaczność.


Jeden szczególny przypadek Pythonie 2 vs 3 zgodność, że podczas gdy stwierdza się, że Python 2.7 documentation

Postać exec(expr, globals) odpowiada exec expr in globals, podczas gdy postać exec(expr, globals, locals) odpowiada exec expr in globals, locals. Krotki formularz exec zapewnia kompatybilność z Python 3, gdzie exec jest funkcją, a nie oświadczeniem.

Krotka forma nie zawsze była w 100% kompatybilna, ponieważ była tam a bug in handling of exec in functions with nested functions (issue 21591); do Python 2.7.8 następujący kod mógł spowodować wyjątek:

def func(): 
    exec('print "hi from test2"', {}) 
    def subfunction(): 
     return None 

Naprawiono to w Pythonie 2.7.9 i już nie rzuca.

+1

Aby uniknąć niefortunnej wersji Python2/Python3 * exec * bugginess i kompatybilności, możesz również użyć idiomu 'edict = {}; eval (kompilacja (instrukcja, "", "exec"), globals(), edict) '. Daje to dostęp do efektów ubocznych * exec * (zobacz także [this discussion of exec/eval/compile] (http://stackoverflow.com/a/29456463/2127439)). – wolfmanx

Powiązane problemy