Zamknięcie Pythona: zapis do zmiennej w zakresie rodzica
Mam następujący kod wewnątrz funkcji:
stored_blocks = {}
def replace_blocks(m):
block = m.group(0)
block_hash = sha1(block)
stored_blocks[block_hash] = block
return '{{{%s}}}' % block_hash
num_converted = 0
def convert_variables(m):
name = m.group(1)
num_converted += 1
return '<%%= %s %%>' % name
fixed = MATCH_DECLARE_NEW.sub('', template)
fixed = MATCH_PYTHON_BLOCK.sub(replace_blocks, fixed)
fixed = MATCH_FORMAT.sub(convert_variables, fixed)
Dodawanie elementów do stored_blocks
działa dobrze, ale nie mogę zwiększyć num_converted
w drugiej podfunkcji:
UnboundLocalError: zmienna lokalna 'num_converted' odwołująca się przed przypisaniem
Mógłbym użyć global
ale zmienne globalne są brzydkie i naprawdę nie potrzebuję tej zmiennej, aby w ogóle była globalna.
Więc jestem ciekaw jak mogę napisać do zmiennej w zakresie funkcji rodzica.
nonlocal num_converted
prawdopodobnie zrobiłby zadanie, ale potrzebuję rozwiązania, które działa z Pythonem 2.x.
6 answers
Problem: dzieje się tak dlatego, że reguły zakresów Pythona są obłąkane. Obecność +=
operatora przypisania oznacza cel num_converted
jako lokalny do zakresu funkcji zamykającej, a w Pythonie 2 nie ma ścieżki dźwiękowej.x, aby uzyskać dostęp tylko do jednego poziomu zasięgu stamtąd. Tylko słowo kluczowe global
może podnieść odwołania do zmiennych z bieżącego zakresu i zabierze cię prosto na górę.
Fix: zamienia num_converted
w tablicę jednoelementową.
num_converted = [0]
def convert_variables(m):
name = m.group(1)
num_converted[0] += 1
return '<%%= %s %%>' % name
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-09 09:00:01
(Zobacz poniżej edytowaną odpowiedź)
Możesz użyć czegoś w rodzaju:
def convert_variables(m):
name = m.group(1)
convert_variables.num_converted += 1
return '<%%= %s %%>' % name
convert_variables.num_converted = 0
W ten sposób, num_converted
działa jako "statyczna" zmienna typu C metody convert_variable
(edytowane)
def convert_variables(m):
name = m.group(1)
convert_variables.num_converted = convert_variables.__dict__.get("num_converted", 0) + 1
return '<%%= %s %%>' % name
W ten sposób nie musisz inicjalizować licznika w głównej procedurze.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-10-03 16:51:37
Użycie słowa kluczowego global
jest w porządku. Jeśli napiszesz:
num_converted = 0
def convert_variables(m):
global num_converted
name = m.group(1)
num_converted += 1
return '<%%= %s %%>' % name
... num_converted
nie staje się "zmienną globalną" (tzn. nie staje się widoczna w żadnych innych nieoczekiwanych miejscach), to po prostu oznacza, że można ją modyfikować wewnątrz convert_variables
. Wydaje się, że dokładnie tego chcesz.
Mówiąc Inaczej, num_converted
jest już zmienną globalną. Składnia global num_converted
mówi Pythonowi: "wewnątrz tej funkcji nie twórz lokalnej zmiennej num_converted
, zamiast tego używaj istniejącej globalnej.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-01-31 13:47:45
A co z użyciem instancji klasy do utrzymywania stanu? Tworzysz instancję klasy i przekazujesz metody instancji do subs, a te funkcje będą miały odniesienie do self...
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-01-31 13:46:26
Mam kilka uwag.
Po pierwsze, jedna aplikacja dla takich zagnieżdżonych funkcji pojawia się podczas radzenia sobie z nieprzetworzonymi wywołaniami zwrotnymi, jak są używane w bibliotekach takich jak xml.parsery.expat. (To, że autorzy biblioteki wybrali takie podejście może być oburzające, ale.. jednak istnieją powody, aby z niego korzystać.)
Po Drugie: wewnątrz klasy są znacznie ładniejsze alternatywy dla tablicy (num_converted[0]). Przypuszczam, że o tym mówił Sebastjan.
class MainClass:
_num_converted = 0
def outer_method( self ):
def convert_variables(m):
name = m.group(1)
self._num_converted += 1
return '<%%= %s %%>' % name
It ' s still odd enough aby zasłużyć na komentarz w kodzie... Ale zmienna jest co najmniej lokalna dla klasy.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-27 13:26:33
Zmodyfikowano z: https://stackoverflow.com/a/40690954/819544
Możesz wykorzystać moduł inspect
, Aby uzyskać dostęp do dict globals calling scope i zapisać do niego. Oznacza to, że ta sztuczka może być nawet wykorzystana, aby uzyskać dostęp do zakresu wywołania z zagnieżdżonej funkcji zdefiniowanej w zaimportowanej podmodule.
import inspect
def get_globals(scope_level=0):
return dict(inspect.getmembers(inspect.stack()[scope_level][0]))["f_globals"]
num_converted = 0
def foobar():
get_globals(0)['num_converted'] += 1
foobar()
print(num_converted)
# 1
Baw się argumentem scope_level
w razie potrzeby. Ustawienie scope_level=1
działa dla funkcji zdefiniowanej w podmodule, scope_level=2
dla funkcji wewnętrznej zdefiniowanej w dekoratorze w submodule itp.
NB: tylko dlatego, że ty możesz to zrobić, nie znaczy, że powinieneś.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-02 00:00:43