関数内に次のコードがあります:
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)
stored_blocks
への要素の追加は正常に機能しますが、2番目のサブ関数でnum_converted
を増やすことはできません。
UnboundLocalError:割り当て前に参照されるローカル変数 'num_converted'
global
を使用できますが、グローバル変数はいため、その変数がグローバルである必要はまったくありません。
したがって、親関数のスコープ内の変数にどのように書き込むことができるのか興味があります。 nonlocal num_converted
はおそらく仕事をしますが、Python 2.x.
問題:これは、Pythonのスコープ規則が認知されていないためです。 +=
代入演算子の存在は、ターゲットnum_converted
を囲んでいる関数のスコープに対してローカルとしてマークし、Python 2.x to global
キーワードのみが現在のスコープから変数参照を持ち上げることができ、トップに直接移動できます。
修正:num_converted
を単一要素の配列に変換します。
num_converted = [0]
def convert_variables(m):
name = m.group(1)
num_converted[0] += 1
return '<%%= %s %%>' % name
(編集された回答については以下を参照)
次のようなものを使用できます。
def convert_variables(m):
name = m.group(1)
convert_variables.num_converted += 1
return '<%%= %s %%>' % name
convert_variables.num_converted = 0
こちらです、 num_converted
は、convert_variableメソッドのCのような「静的」変数として機能します。
(編集済み)
def convert_variables(m):
name = m.group(1)
convert_variables.num_converted = convert_variables.__dict__.get("num_converted", 0) + 1
return '<%%= %s %%>' % name
このように、メインプロシージャでカウンターを初期化する必要はありません。
global
キーワードを使用しても問題ありません。あなたが書く場合:
num_converted = 0
def convert_variables(m):
global num_converted
name = m.group(1)
num_converted += 1
return '<%%= %s %%>' % name
... num_converted
は「グローバル変数」にはなりません(つまり、他の予期しない場所では表示されません)。これは、convert_variables
内で変更できることを意味します。それはまさにあなたが望むもののようです。
別の言い方をすれば、num_converted
はalreadyグローバル変数です。すべてのglobal num_converted
構文はtell Python "この関数の内部では、ローカルnum_converted
変数を作成せずに、既存のグローバル変数を使用します。
クラスインスタンスを使用して状態を保持するのはどうですか?クラスをインスタンス化し、インスタンスメソッドをsubsに渡すと、それらの関数はselfへの参照を持ちます...
いくつかの発言があります。
まず、xml.parsers.expatなどのライブラリで使用されているように、生のコールバックを処理するときに、このようなネストされた関数の1つのアプリケーションが表示されます。 (ライブラリ作成者がこのアプローチを選択したことは好ましくないかもしれませんが、それでも…それを使用する理由があります。)
2番目:クラス内では、配列(num_converted [0])に代わるより優れた選択肢があります。これがセバスチャンが言っていたことだと思います。
class MainClass:
_num_converted = 0
def outer_method( self ):
def convert_variables(m):
name = m.group(1)
self._num_converted += 1
return '<%%= %s %%>' % name
コード内でコメントに値するのはまだ奇妙です...しかし、変数は少なくともクラスに対してローカルです。
変更元: https://stackoverflow.com/a/40690954/819544
inspect
モジュールを活用して、呼び出しスコープのグローバルdictにアクセスし、そこに書き込むことができます。つまり、このトリックを利用して、インポートされたサブモジュールで定義されたネストされた関数から呼び出しスコープにアクセスすることさえできます。
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
scope_level
引数は必要に応じて。設定scope_level=1
は、サブモジュールで定義された関数、scope_level=2
サブモジュールなどのデコレータで定義された内部関数.
注意:canできるからといって、そうすべきだとは限りません。