Pythonにおける名前空間とスコープを理解する上でのポイントを押さえよう特集:Visual Studioで始めるPythonプログラミング(4/4 ページ)

» 2016年12月09日 05時00分 公開
[かわさきしんじInsider.NET編集部]
前のページへ 1|2|3|4       

global/nonlocalキーワード

 Pythonにはglobal/nonlocalという他の言語ではあまり見られないキーワードがある。これらは現在のスコープからグローバルスコープあるいは自分を囲むスコープに存在する変数への再代入を可能にする。

 先ほどの「関数内でグローバル変数と同じ名前の変数に代入をしてみる」例では、グローバル変数の値が変わるのではなく、ローカルスコープに新たに名前が導入されたが、globalキーワードを付けて変数を宣言することで(global文)、その名前はグローバルスコープに存在するものとして取り扱われる。

 globalキーワードの使用例を以下に示す。

name = "insider.net"
def test():
  global name
  name = "windows server insider"
  print(locals())

test()
print(name)


globalキーワードの使用例

 globalキーワードには続けて、グローバルスコープに存在するとして扱う名前を記述する。「global name = ……」のようにグローバル変数の宣言と代入(再束縛)を同時にはできないので注意しよう。

 実行結果は次のようになる。

>>> name = "insider.net"
>>> def test():
...   global name
...   name = "windows server insider"
...   print(locals())
...
>>> test()
{}
>>> print(name)
windows server insider


ローカルスコープに名前nameはなく、グローバル変数nameの値が書き換えられた

 関数内部での組み込み関数localsの呼び出しを見ると分かる通り、ローカルスコープにはnameという名前は存在していない。その一方で「name = "windows server insider"」行によりグローバル変数nameの値が変化している(「windows server insider」に再束縛された)ことが分かる。

 なお、上の例では最初にグローバル変数nameが確実に存在しているように「name = "insider.net"」行を実行しているが、globalキーワードに続けて記述する名前は宣言時にグローバルスコープに存在している必要はない。よって、上の状態から以下のコードを実行してもエラーは発生しない(実行結果は割愛)。

del name
test()
print(name)


グローバル変数nameがなくてもglobalキーワードの使用はエラーにはならない

 もう1つのキーワードnonlocalはローカルスコープを囲むスコープに存在する名前に対して、ローカルスコープから代入を行えるようにするものだ。以下に例としてカウンターを作成する関数を示す。最初にエラーとなる例を示そう。

def makecounter():
  n = 0
  def count():
    n += 1
    return n
  return count

counter = makecounter()
print(counter())


カウンター関数を作成する関数makecounter

 これは典型的なクロージャであり、外側のスコープで定義されている変数nの値を、内側の関数で参照、変更している。実際にこれを利用しようとすると次のようになる。

>>> def makecounter():
...   n = 0
...   def count():
...     n += 1
...     return n
...   return count
...
>>> counter = makecounter()
>>> print(counter())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 4, in count
UnboundLocalError: local variable 'n' referenced before assignment


内側の関数count内では変数nが未定義

 内側の関数ではローカルスコープの外部にある名前を参照はできるが、その値を変更することはできない(上のglobalキーワードの例では、代入しようとすると同じ名前が新たにローカルスコープに導入されていた。今回は導入される前に、その値を変更しようとしている)。そのため、「n += 1」行は「未定義の変数の値を変更しようとした」と解釈されて、実行時に「UnboundLocalError」例外が発生している。こうした状況を避けるためにnonlocalキーワードが利用できる。上のコードを修正すると次のようになる。

def makecounter():
  n = 0
  def count():
    nonlocal n
    n += 1
    return n
  return count

counter = makecounter()
print(counter())
print(counter())
print(counter())


修正後のコード

 「nonlocal n」行で名前nがローカルスコープを囲むスコープに存在していることを明示することで、その値を変更できるようになった。実際の実行結果を以下に示す(確かに動作しているかを確認するために「print(counter())」行の数も増やしている)。

>>> def makecounter():
...   n = 0
...   def count():
...     nonlocal n
...     n += 1
...     return n
...   return count
...
>>> counter = makecounter()
>>> print(counter())
1
>>> print(counter())
2
>>> print(counter())
3


内側の関数countが変数nの値を変更できていることが分かる

 このようにクロージャから外側のスコープで定義されている変数の値を変更する必要があるといった場合にはnonlocalは便利に使えるはずだ。また、ここでは組み込み関数localsの呼び出しは行っていないが、前ページの最後の例と同様な結果となる(つまり、外側のスコープで定義されている変数が「自由変数」としてローカルな名前空間に存在するものとして扱われていることが分かる)。


 今回はPythonにおけるスコープと名前空間に関連する事項を取り上げた。次回はPythonの例外を取り上げる。なお、PTVSの[Interactive]ウィンドウは本稿で見たように変数__builtins__が参照するオブジェクトが通常の対話環境と異なるが、その一方で対話環境のスコープを__main__モジュール以外のものに変更できる。興味のある方はPTVSのリポジトリ内にあるドキュメント「Interactive REPL」(英語)などを参照されたい。

「特集:Visual Studioで始めるPythonプログラミング」のインデックス

特集:Visual Studioで始めるPythonプログラミング

前のページへ 1|2|3|4       

Copyright© Digital Advantage Corp. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。