4
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【Python】【スコープ】globalとnonlocalとは?!

Posted at

 僕がPythonを勉強し始めて最初に「何だこれ?」と感じたのが「global」と「nonlocal」でした。

 簡単に説明するなら、どちらも「同じ変数を関数の中でも外でも使いたい時」に用いるものです。そして関数が入れ子構造で定義されているときに「nonlocal」が使われるというイメージです。

 しかしそんなこと言ってもいまいち理解できないため、ひとまず実際に「global」を用いたプログラムを作ってみました。

 それが、こちらのクリック数をカウントするプログラムです。

Image from Gyazo

 コードはこのようになっています。あくまでも「global」の必要性を確認するために作ったので、ディテールには全くこだわっていません。

from tkinter import *

count = 1

def click_count():
    global count
    txt.insert(0,count)
    txt.delete(len(str(count)),END)
    count += 1

win = Tk()
cv = Canvas(win, width = 300, height = 200)
win.title('クリック数をカウントする')
cv.pack()

txt = Entry(width = 3)
txt.place(x = 130, y = 50)

btn = Button(text='ボタン', command = click_count)
btn.place(x = 125, y = 100)

##なぜ「global」が必要なのか?
 このプログラムがやりたいことは、「ボタンをクリックしたらカウントが増える」というものです。単純な理屈としては以下のようなことがやりたかったわけです。ボタンを押すというアクションを起こすたびにこの関数を呼び出すのです。

count = 1
def click_count():
    txt.insert(0,count)
    count += 1

 しかしこのようなことをすると以下のようなエラーが起きます。

UnboundLocalError: local variable 'count' referenced before assignment

 関数の中で、関数の外の「count」という変数を操作することはできないということです。変数にはどうやら「スコープ」という、使用できる範囲が決められているみたいなのです。
 では関数の中で定義するしかないのかと考え、以下のようなことをしてしまうと、当然ながら何度ボタンを押しても1しか表示されなくなります。

def click_count():
    count = 1
    txt.insert(0,count)
    count += 1

 そんな時に使えるのが「global」というわけです。

count = 1
def click_count():
    global count
    txt.insert(0,count)
    count += 1

 このような記述をしておけば、関数の外にある変数も操作できて一件落着なのです。

##では「nonlocal」とは何か?
 しかしながら、「global」も、適用される範囲が限られています。入れ子構造で関数が定義されている場合を見てみましょう。
 以下のサイトを参考にしてプログラムを作ってみました。

def scope():
    loc = "init"

    def do_global():
        global loc
        loc = "global"
        
    do_global()
    print("A",loc)

scope()
print("B",loc)

 こちらのコードの出力結果はどのようになると考えられるでしょうか?「globalを使えば関数の外でも中でも同じ変数が使えるんだ!」っていう理屈を単純に考えれば、出力結果は以下のように予想できます。

A global
B global

 ところが実際の出力結果はこうでした。

A init
B global

 ①do_globalの内側 < ②scope()の内側 < ③scope()の外側と考えた時、①で定義されたglobalの変数は②では使えないみたいです。
 入れ子構造で関数が定義されている時、この②scope()の内側で使える変数を定義できるようにするものが「nonlocal」なのです。
 

def scope():
    loc = "init"

    def do_nonlocal():
        nonlocal loc
        loc = "nonlocal"

    def do_global():
        global loc
        loc = "global"

    do_nonlocal()
    print("A", loc)
    do_global()
    print("B",loc)

scope()
print("C",loc)

 このようにdo_nonlocal()という関数を定義して呼び出した時、出力結果は以下のようになるのです。

A nonlocal
B nonlocal
C global

 do_nonlocal()を呼び出して以降、たとえdo_global()を呼び出そうが、②scope()の内側で定義されている変数「loc」は「nonlocal」という値になったのです。

 ただし、当然ながらdo_nonlocal()を呼び出す前は、scope()の中でのlocの値は「init」であるということにも注意が必要です。

def scope():
    loc = "init"
    print("A",loc)

    def do_local():
        loc = "local"

    def do_nonlocal():
        nonlocal loc
        loc = "nonlocal"

    def do_global():
        global loc
        loc = "global"

    do_local()
    print("B",loc)
    do_nonlocal()
    print("C", loc)
    do_global()
    print("D",loc)

scope()
print("E",loc)

 このように記述を増やした場合の出力結果は以下のようになります。

A init
B init
C nonlocal
D nonlocal
E global

 
 自分自身の理解を定着される目的でこのような記事を書きました。もし気になることがございましたらご指摘ください。


 

4
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?