変数の種類とスコープ#
教科書 p.197(Lesson 7.6)関数を定義するようになると、関数の中で使う変数と、関数の外で使う変数が混在することがあります。 このうち、関数内で名前が決められ、利用される変数を ローカル変数(Local Variable) と呼びます。 関数の仮引数もローカル変数です。
一方、関数の外で名前が決められ、利用される変数を グローバル変数(Global Variable) と呼びます。 どちらも有効範囲があり、これを スコープ(Scope) と呼びます。 ローカル変数の有効範囲は、その変数が定義された関数内部であり、 グローバル変数の有効範囲は、その変数が定義されたファイル内全体です。
def add(a, b): # a, b は仮引数で、ローカル変数。
c = a + b # c はローカル変数。
return c
def subtract(a, b): # a, b は仮引数で、ローカル変数。
c = a - b # c はローカル変数だけど、addでのcとは別の変数。
return c # 関数が異なると、例え名前が同じでも、ローカル変数は全く異なる変数として扱われる。
def multiple(a, b):
d = a * b # 関数の中からは、他の関数の変数(c)にはアクセスできない。
return d # a, b はたまたま同じ名前だが、他の関数の a, b とは全く別物。
def divide(a, b):
if b == 0:
return "0で割ることはできません。"
d = a // b
return d
x = 10 # これ以降の変数は関数に所属していないため、グローバル変数。
y = 5 # 関数の外なので、関数内で定義されたローカル変数(a, b, c, d)にはアクセスできない。
print(f"{x} + {y} = {add(x, y)}")
print(f"{x} - {y} = {subtract(x, y)}")
print(f"{x} * {y} = {multiple(x, y)}")
print(f"{x} // {y} = {divide(x, y)}")このプログラムでは、add 関数、subtract 関数、multiple 関数、divide 関数の中で a, b という変数が使われています。
また、add 関数、subtract 関数では c、multiple 関数、divide 関数では d という変数が使われています。
これらの変数は、それぞれの関数内で定義されているため、ローカル変数です。
ローカル変数は関数内でしか有効ではなく、関数の外側では利用できません。
グローバル変数を利用する#
一方、x と y は関数の外で定義されているため、グローバル変数です。
グローバル変数はファイル内のどこでも(関数内部でも)利用できます。
ただし、関数の中でグローバル変数を 読み取る のと、再代入する のとでは扱いが異なります。
- 読み取るだけなら、何もしなくても関数内からグローバル変数を参照できます。
- 関数内で
a = 1のように代入すると、そのaは 新しいローカル変数 になります。同じ名前のグローバル変数があっても、それとは別物です。 - 関数内からグローバル変数に再代入したいときは、
global aと宣言します。この宣言があると、その関数内のaはグローバル変数aを指します。 - リストのように中身を変更できるオブジェクトの場合、
items.append(x)のように 中身を変える 操作にはglobalは不要です。名前itemsを別のオブジェクトに付け替えるわけではないからです。
a = 0
items = []
def read_global():
print(f"read_global: a = {a}") # 読み取るだけなら global は不要。
def assign_local():
a = 1 # 関数内で代入すると、この a は新しいローカル変数になる(グローバル変数 a とは別物)。
print(f"assign_local: a = {a}")
def assign_global():
global a # グローバル変数 a に再代入することを宣言する。
a = 10 # global を書いたので、これはグローバル変数 a への代入。
print(f"assign_global: a = {a}")
def append_item(x):
items.append(x) # 名前 items を付け替えるのではなく、items が指すリストの中身を変えている。global は不要。
print(f"append_item: items = {items}")
print(f"global a = {a}, items = {items}")
read_global()
assign_local()
print(f"global a = {a} after assign_local")
assign_global()
print(f"global a = {a} after assign_global")
append_item(5)
print(f"global items = {items} after append_item")実行結果#
$ python3 scope2.py
global a = 0, items = []
read_global: a = 0
assign_local: a = 1
global a = 0 after assign_local
assign_global: a = 10
global a = 10 after assign_global
append_item: items = [5]
global items = [5] after append_itemassign_local の中の a = 1 はグローバル変数 a を変更していない(after assign_local でも 0 のまま)のに対し、
global a を宣言した assign_global はグローバル変数 a を 10 に変えています。
また、append_item は global なしでグローバル変数 items が指すリストの中身を変えています。
なお、関数の外の変数を関数内から書き換えるプログラムは、どこで値が変わるのかが追いにくくなります。
関数には必要な値を引数で渡し、結果は返り値で受け取るのが基本です。global を使うのは、それでは書けない場合に限りましょう。
ローカル変数一覧#
教科書 p.203 (Lesson 7.6)dir関数は、関数内で利用すると、その関数で定義されているローカル変数一覧を取得できます。
デバッグ時などに利用すると便利です。
def func():
a = 1
b = 2
c = 3
print(f"func: {dir()}")
func()実行結果#
$ python3 scope3.py
func: ['a', 'b', 'c'] # func で定義されているローカル変数の一覧変数の寿命#
教科書 p.204 (Lesson 7.7)Python の変数は、名前とオブジェクト(値)を結びつけたものです。 「変数の寿命」を考えるときは、名前がいつまで使えるか(スコープ) と、オブジェクトがいつまで存在するか を分けて考える必要があります。
ローカル変数の名前は、関数の中で代入された時点から使えるようになり、関数が終了すると使えなくなります。
ただし、その名前が指していたオブジェクトまで消えるとは限りません。
関数が return で返したオブジェクトや、他の変数からも参照されているオブジェクトは、関数が終わった後も使い続けられます。
どこからも参照されなくなったオブジェクトは、Python が自動的にメモリから解放します。
グローバル変数の名前は、プログラムの開始前にまとめて用意されるのではなく、ファイルの先頭から順に実行されていく中で、代入文が実行された時点で使えるようになります。 そのため、関数の中でグローバル変数を参照する場合は、その関数が 呼び出される時点 でその名前に値が代入されていれば利用できます。
def make_list(n):
result = [] # result はローカル変数。
for i in range(n):
result.append(i * i)
return result # result が指しているリストを返す。
squares = make_list(5) # 関数は終了し、名前 result はもう使えないが、
print(squares) # リストそのものは squares という名前で使い続けられる。
def show():
print(f"show: total = {total}") # total は関数の呼び出し時に探される。
total = 100 # ここで初めて名前 total が値に結びつく。
show() # この時点では total が存在するので使える。実行結果#
$ python3 scope4.py
[0, 1, 4, 9, 16]
show: total = 100make_list の中のローカル変数 result は、関数が終わると名前としては使えなくなりますが、
result が指していたリストは squares という名前で受け取っているので、そのまま使えます。
一方、show の呼び出しを total = 100 より前に移動すると、その時点では名前 total がまだ存在しないため、NameError: name 'total' is not defined となります。
