Ich habe den folgenden Code in einer Funktion:
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)
Das Hinzufügen von Elementen stored_blocks
funktioniert einwandfrei, aber ich kann num_converted
die zweite Unterfunktion nicht erhöhen :
UnboundLocalError: Lokale Variable 'num_converted', auf die vor der Zuweisung verwiesen wird
Ich könnte verwenden, global
aber globale Variablen sind hässlich und ich brauche diese Variable wirklich nicht, um überhaupt global zu sein.
Ich bin also gespannt, wie ich in eine Variable im Bereich der übergeordneten Funktion schreiben kann.
nonlocal num_converted
würde wahrscheinlich den Job machen, aber ich brauche eine Lösung, die mit Python 2.x funktioniert.
python
closures
scope
python-2.x
DiebMaster
quelle
quelle
def
ist dies nicht das einzige Schlüsselwort, das einen Namespace definiert: Es gibt auchclass
.Antworten:
Problem: Dies liegt daran, dass die Scoping-Regeln von Python dement sind. Das Vorhandensein des
+=
Zuweisungsoperators markiert das Zielnum_converted
als lokal für den Bereich der umschließenden Funktion, und in Python 2.x gibt es keine solide Möglichkeit, von dort aus auf nur eine Bereichsebene zuzugreifen. Nur dasglobal
Schlüsselwort kann variable Referenzen aus dem aktuellen Bereich entfernen und bringt Sie direkt nach oben.Fix: Verwandeln Sie sich
num_converted
in ein Einzelelement-Array.num_converted = [0] def convert_variables(m): name = m.group(1) num_converted[0] += 1 return '<%%= %s %%>' % name
quelle
+=
Zuweisungsoperators markiert das Zielnum_converted
als lokal für den Bereich der umschließenden Funktion, und in Python 2.x gibt es keine solide Möglichkeit, von dort aus auf nur eine Bereichsebene zuzugreifen. Nur dasglobal
Schlüsselwort kann variable Referenzen aus dem aktuellen Bereich entfernen und bringt Sie direkt nach oben.global
bedeutet nicht, dass Sie keine globale Variable haben.scope = {'num_converted':0} … scope['num_converted'] += 1
.(siehe unten für die bearbeitete Antwort)
Sie können etwas verwenden wie:
def convert_variables(m): name = m.group(1) convert_variables.num_converted += 1 return '<%%= %s %%>' % name convert_variables.num_converted = 0
Auf diese Weise
num_converted
funktioniert es als C-ähnliche "statische" Variable der Methode convert_variable(bearbeitet)
def convert_variables(m): name = m.group(1) convert_variables.num_converted = convert_variables.__dict__.get("num_converted", 0) + 1 return '<%%= %s %%>' % name
Auf diese Weise müssen Sie den Zähler im Hauptverfahren nicht initialisieren.
quelle
convert_variables.num_converted
Die Verwendung des
global
Schlüsselworts ist in Ordnung. Wenn Sie schreiben:num_converted = 0 def convert_variables(m): global num_converted name = m.group(1) num_converted += 1 return '<%%= %s %%>' % name
...
num_converted
wird keine "globale Variable" (dh sie wird an keiner anderen unerwarteten Stelle sichtbar), sondern bedeutet nur, dass sie im Inneren geändert werden kannconvert_variables
. Das scheint genau das zu sein, was Sie wollen.Anders ausgedrückt,
num_converted
ist bereits eine globale Variable. Dieglobal num_converted
Syntax sagt Python lediglich: "Erstellen Sie in dieser Funktion keine lokalenum_converted
Variable, sondern verwenden Sie die vorhandene globale.quelle
global
in 2.x funktioniert so ziemlich wienonlocal
in 3.x.Was ist mit einer Klasseninstanz, um den Status zu halten? Sie instanziieren eine Klasse und übergeben Instanzmethoden an Subs, und diese Funktionen haben einen Verweis auf self ...
quelle
Ich habe ein paar Bemerkungen.
Erstens wird eine Anwendung für solche verschachtelten Funktionen beim Umgang mit unformatierten Rückrufen angezeigt, wie sie in Bibliotheken wie xml.parsers.expat verwendet werden. (Dass die Autoren der Bibliothek diesen Ansatz gewählt haben, mag zu beanstanden sein, aber ... es gibt dennoch Gründe, ihn zu verwenden.)
Zweitens: Innerhalb einer Klasse gibt es viel schönere Alternativen zum Array (num_converted [0]). Ich nehme an, das war es, worüber Sebastjan sprach.
class MainClass: _num_converted = 0 def outer_method( self ): def convert_variables(m): name = m.group(1) self._num_converted += 1 return '<%%= %s %%>' % name
Es ist immer noch seltsam genug, um einen Kommentar im Code zu verdienen ... Aber die Variable ist zumindest lokal für die Klasse.
quelle
Geändert von: https://stackoverflow.com/a/40690954/819544
Sie können das
inspect
Modul nutzen, um auf das globale Diktat des aufrufenden Bereichs zuzugreifen und in dieses zu schreiben. Das bedeutet, dass dieser Trick sogar genutzt werden kann, um über eine in einem importierten Submodul definierte verschachtelte Funktion auf den aufrufenden Bereich zuzugreifen.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
Spielen Sie
scope_level
nach Bedarf mit dem Argument. Die Einstellungscope_level=1
funktioniert für eine in einem Submodul definierte Funktion,scope_level=2
für die in einem Dekorator in einem Submodul definierte innere Funktion usw.NB: Nur weil Sie das können , heißt das nicht, dass Sie es sollten.
quelle