この記事では、Pythonのグローバル変数の使い方について、基本から注意点、そして「えっ、そんな落とし穴があったの!?」というポイントまで、実例を交えて分かりやすく解説していきます!
グローバル変数って、プログラムのどこからでもアクセスできる便利なやつ…と思いきや、使い方を間違えるとコードがぐちゃぐちゃになる原因にもなりかねません。
この記事を読めば、グローバル変数の仕組みをしっかり理解し、自信を持ってコードを書けるようになりますよ。もうグローバル変数で悩むのはおしまいにしましょう!
この記事で学べること
- グローバル変数ってそもそも何?
- ローカル変数との決定的な違い
global
キーワードの正しい使い方- グローバル変数が嫌われる本当の理由
- よくあるエラーとその解決方法
- グローバル変数を使わないスマートな方法
さあ、一緒にグローバル変数の世界を探検してみましょう!
Pythonのグローバル変数とは?
Pythonにおけるグローバル変数とは、プログラムのどこからでもアクセスできる変数のことです。関数の中とか外とか、場所を選ばずに使える、いわばプログラム全体で共有されるメモ用紙みたいなイメージですね。
例えば、ゲーム全体で使うプレイヤーのスコアとか、アプリケーションの設定情報みたいに、あちこちの処理で共通して使いたい値を入れておくのに使われたりします。
これに対して、関数の中で定義される変数は「ローカル変数」と呼ばれて、その関数の中でしか使えません。関数の外に出たら、その変数はもう存在しない、みたいな感じです。グローバル変数は、このローカル変数の「縄張り」を越えて、プログラム全体が活動範囲になるんですね。
なぜこういう仕組みがあるかというと、プログラムの色々な部分で共通のデータを使いたい場面があるからです。ただし、便利さの裏には注意点もあるので、まずは基本を押さえていきましょう。
グローバル変数とローカル変数の違いはスコープにあり
グローバル変数とローカル変数の大きな違いは、変数が有効な範囲、つまり「スコープ」にあります。変数がどの範囲で認識されて使えるか、という縄張りのようなものだと考えてみてください。
グローバルスコープ
プログラムの一番外側(どの関数にも属さない場所)で定義された変数は、グローバルスコープを持ちます。これがグローバル変数です。プログラムのどこからでもアクセス可能です。
ローカルスコープ
関数の中で定義された変数は、ローカルスコープを持ちます。これがローカル変数です。その関数の中だけで有効で、関数の外からはアクセスできません。関数が終わると、基本的には消えてしまいます。
ちょっと図でイメージしてみましょう。
+-----------------------------------------+ <-- グローバルスコープ (プログラム全体) | | | global_var = 100 (グローバル変数) | | | | def my_function(): | | +-----------------------------------+ <-- ローカルスコープ (my_functionの中) | | | | | local_var = 10 (ローカル変数) | | | | | | print(local_var) # OK | | | print(global_var) # OK | | | | | +-----------------------------------+ | | | my_function() | | print(global_var) # OK | | # print(local_var) # NG! エラーになる | | | +-----------------------------------------+
コードで見るとこんな感じです。
# グローバル変数の例 message = "これはグローバル変数です" def my_function(): # ローカル変数の例 local_message = "これはローカル変数です" print(local_message) # 関数の中なのでローカル変数を表示できる print(message) # 関数の中からグローバル変数を参照することもできる # 関数の実行 my_function() # 関数の外から変数を参照 print(message) # グローバル変数は外からもOK # print(local_message) # これはエラー!関数の外からはローカル変数にアクセスできない
このスコープの違いを理解することが、グローバル変数を使いこなす第一歩です。
グローバル変数を参照する基本的な使い方
関数の中からグローバル変数の値を「読み取る」だけなら、実は特別なことは何もする必要がありません。
Pythonは賢いので、関数の中で変数を使おうとしたとき、まず自分の持ち物(ローカルスコープ)にその名前の変数があるか探します。もし無ければ、次に外の世界(グローバルスコープ)に同じ名前の変数がないか探しに行くんですね。
だから、グローバルスコープに変数があれば、関数の中から普通にその名前を呼ぶだけで値を使えます。
例を見てみましょう。
player_name = "勇者" # グローバル変数 def display_status(): # 関数内で特別な宣言なしにグローバル変数を参照 print(f"プレイヤー名: {player_name}") display_status() # 関数を実行
このコードでは、display_status
関数の中でplayer_name
というグローバル変数をそのまま使って表示しています。読み取るだけなら、これでOKなんです。簡単ですよね!
グローバル変数を変更する - globalキーワードの使い方
さて、ここからが少しややこしくなるポイントです。関数の中でグローバル変数の値を「変更(書き換え)」したい場合は、ただ変数名を書くだけではダメなんです。
もし関数の中でグローバル変数と同じ名前の変数に値を代入しようとすると、Pythonは「お、新しいローカル変数を作るんだな」と解釈してしまいます。つまり、グローバル変数を変更するつもりが、同名の新しいローカル変数を作ってしまうことになるんです。
そこで登場するのが global
キーワードです!
関数の中で「今から使うこの変数は、外にあるグローバル変数のことですよ!」とPythonに教えてあげる宣言、それが global
です。これを関数の最初の方で書いておくことで、その変数への代入がグローバル変数の変更として扱われるようになります。
global
を使う場合と使わない場合を比べてみましょう。
score = 100 # グローバル変数 def update_score_wrong(): # global宣言がない場合 score = 50 # これは新しいローカル変数scoreを作って代入している print(f"関数内(NG): score = {score}") # ローカル変数の値が表示される def update_score_correct(): # global宣言をする場合 global score # 「scoreはグローバル変数ですよ」と宣言 score = score + 10 # これでグローバル変数が変更される print(f"関数内(OK): score = {score}") print(f"実行前: score = {score}") update_score_wrong() print(f"NG実行後: score = {score}") # グローバル変数は変わっていない! update_score_correct() print(f"OK実行後: score = {score}") # グローバル変数が変更された!
実行結果を見ると、global
がないと関数内で値が変わっても、外のグローバル変数には影響がないことがわかります。グローバル変数を関数内で変更したいときは、global
を忘れずに書きましょう!
globalを使ったプログラムと実行結果
もう少し具体的な例で global
の使い方を見てみましょう。カウンター(数を数える変数)をグローバル変数として用意し、関数でその数を増やすプログラムです。
# --- ソースコード --- count = 0 # グローバル変数としてカウンターを初期化 def increment(): # 関数内でグローバル変数countを変更することを宣言 global count count += 1 # count = count + 1 と同じ意味 print(f"関数内: count = {count}") print(f"実行前: count = {count}") # 関数を2回呼び出してカウントアップ increment() increment() print(f"実行後: count = {count}")
これを実行すると、次のような結果が表示されます。
# --- ソースコードの表示結果 --- 実行前: count = 0 関数内: count = 1 関数内: count = 2 実行後: count = 2
ちゃんと関数を呼び出すたびに、外にある count
の値が増えているのが確認できますね。global count
の宣言のおかげで、関数の中からグローバル変数を正しく変更できています。これが global
キーワードの基本的な使い方です。
なぜPythonのグローバル変数の利用は推奨されないのか?
ここまでグローバル変数の使い方を見てきましたが、実は多くの経験豊富なプログラマーは、グローバル変数の使用をできるだけ避けるべき、と考えています。
なぜでしょうか? 便利そうに見えるのに、嫌われてしまうのには理由があります。
- コードが読みにくくなる
グローバル変数はどこからでもアクセスできるため、プログラムが大きくなると「この変数はどこで定義されて、どこで変更されているんだっけ?」と追跡するのが大変になります。まるで部屋中に散らばったメモを探すようなものです。 - 意図しない副作用を生みやすい
ある関数がグローバル変数を変更したら、それを予期していなかった別の関数が影響を受けてしまう可能性があります。気づかないうちに値が変わっていて、バグの原因になることが多いんです。 - デバッグが難しくなる
何か問題が起きたとき、その原因がグローバル変数の予期せぬ変更だった場合、どの関数が問題を引き起こしたのか特定するのが困難になります。 - プログラムの部品化(モジュール化)がしにくい
グローバル変数に依存している関数は、そのグローバル変数が無い別のプログラムで再利用するのが難しくなります。関数が「自立」していない状態になってしまうんですね。 - テストがしにくい
関数のテストをする際、グローバル変数の状態も考慮しないといけなくなり、テストケースが複雑になりがちです。
もちろん、設定値のように本当にプログラム全体で共有すべき定数など、グローバル変数が適している場面もゼロではありません。
でも、特に処理の途中で値が変わるような変数を安易にグローバルにするのは、後々の苦労の種になりやすいので注意が必要です。
グローバル変数を使う際の注意点とよくあるエラー
それでもグローバル変数を使う場面が出てくるかもしれません。
そんなときのために、特に注意したい点と、初心者が陥りやすいエラーについて知っておきましょう。
global
宣言のし忘れ
これが一番よくあるミスです。関数内でグローバル変数を変更するつもりで代入したのに、global
宣言を忘れてしまい、意図せず同名のローカル変数が作られてしまうパターン。global
! と覚えておきましょう。名前の衝突
自分で定義したグローバル変数名が、もしかしたらPythonが内部で使っている名前や、他のライブラリが使う名前と偶然かぶってしまう可能性もゼロではありません。多すぎるグローバル変数
便利だからといって何でもかんでもグローバル変数にすると、あっという間に管理が大変になります。UnboundLocalError
これはちょっと特殊なエラーです。関数内でグローバル変数と同じ名前の変数に「代入する前」に、その変数を参照しようとすると発生します。詳細は次の項目で解説しますね。これらの点に気をつけるだけでも、グローバル変数絡みのトラブルを減らせるはずです。
UnboundLocalError: local variable 'xxx' referenced before assignment
というエラーメッセージを見たことはありますか? これは、グローバル変数を扱っているときによく遭遇するエラーの一つです。
このエラーは、関数内でローカル変数として扱われる変数(global
宣言がない場合)に、値が代入されるよりも前に、その変数を読み込もうとしたときに発生します。
どういうことか、例を見てみましょう。
# --- エラーが発生するコード --- counter = 10 # グローバル変数 def show_and_increment(): print(f"現在の値: {counter}") # ← ここでエラー! # この関数内で counter に代入があるので、Pythonは counter をローカル変数とみなす # しかし、この行の時点ではまだローカル変数 counter に値が代入されていないためエラーになる counter += 1 show_and_increment() # この関数を呼び出すと UnboundLocalError が発生
上の例では、show_and_increment
関数の中でcounter += 1
という代入処理があります。この代入があるために、Pythonはこの関数内のcounter
を「ローカル変数」だと判断します。
ところが、その代入が行われる前のprint
文でcounter
を参照しようとしています。Pythonは「まだ値が決まってないローカル変数を表示しろって言われても困るよ!」となり、UnboundLocalError
を出すわけです。
対処法は、関数内でグローバル変数を変更するつもりなら、global
宣言をちゃんと書くことです。
# --- 修正後のコード --- counter = 10 # グローバル変数 def show_and_increment_fixed(): global counter # グローバル変数 counter を使うことを明示 print(f"現在の値: {counter}") # これでOK counter += 1 print(f"増やした後の値: {counter}") show_and_increment_fixed() print(f"関数実行後のグローバル変数: {counter}")
global counter
を追加することで、関数内のcounter
がグローバル変数を指すようになり、エラーは解消されます。もし意図的にローカル変数として使いたいなら、グローバル変数とは別の名前をつけるべきですね。
global宣言なしで関数間で値を共有する方法
グローバル変数は便利だけど、なるべく使わない方がいい…じゃあ、どうすれば関数同士で値を共有できるの? という疑問が湧きますよね。
大丈夫、もっとスマートで安全な方法があります!
グローバル変数に頼らずに関数間でデータをやり取りする主な方法として、以下の二つがよく使われます。
- 関数の引数(ひきすう)と戻り値(もどりち)を使う
- クラスを使って状態を管理する
これらはグローバル変数を使うよりもコードの見通しが良くなり、関数が独立して動きやすくなるため、一般的に推奨されるやり方です。それぞれの方法を具体的に見ていきましょう。
関数の引数と戻り値を使う方法
これが最も基本的で、かつ推奨される方法です。考え方はシンプルです。
- 関数が必要なデータは、関数を呼び出すときに引数として渡してあげる。
- 関数が処理した結果は、戻り値として関数が終わるときに返してもらう。
こうすることで、関数は外部のグローバル変数に依存せず、与えられた情報(引数)だけを使って仕事をし、結果を返す、という独立した部品になります。コードの流れが追いやすくなり、テストもしやすくなります。
例を見てみましょう。スコアを加算する処理を考えます。
def add_score(current_score, points): """現在のスコアにポイントを加算して新しいスコアを返す関数""" new_score = current_score + points print(f"{points}点加算!") # 計算結果を戻り値として返す return new_score # 初期スコア my_score = 100 print(f"最初のスコア: {my_score}") # 関数を呼び出してスコアを更新 # 現在のスコア(my_score)と加算する点数(10)を引数で渡す # 関数の戻り値(新しいスコア)をmy_scoreに再代入する my_score = add_score(my_score, 10) print(f"更新後のスコア: {my_score}") my_score = add_score(my_score, 50) print(f"さらに更新後のスコア: {my_score}")
この方法なら、add_score
関数はグローバル変数を一切気にしていません。必要な情報は引数でもらい、結果は戻り値で返す。非常にクリーンで分かりやすいやり方だと思いませんか?多くの場面で、この方法がグローバル変数の代わりになります。
クラスを使って状態を管理する方法
複数の関数(クラスの中では「メソッド」と呼びます)で、あるまとまった状態(データ)を共有・管理したい場合には、クラスを使うのが効果的です。
クラスは、データ(変数)とそのデータを操作する関数(メソッド)をひとまとめにする設計図のようなものです。この設計図から作られた実体(オブジェクト)が、内部に状態を保持します。
例えば、プレイヤーの体力や持ち物など、関連する複数のデータをまとめて管理したい場合にクラスが役立ちます。
class Player: def __init__(self, name): # __init__ はオブジェクトが作られるときに最初に呼ばれる特別なメソッド # self.変数名 で、オブジェクトが持つ変数(インスタンス変数)を定義 self.name = name self.hp = 100 # 体力 print(f"{self.name} が誕生した! HP: {self.hp}") def heal(self, amount): # HPを回復するメソッド self.hp += amount print(f"{self.name} は {amount} 回復した! 現在のHP: {self.hp}") def take_damage(self, damage): # ダメージを受けるメソッド self.hp -= damage print(f"{self.name} は {damage} のダメージを受けた! 現在のHP: {self.hp}") # Playerクラスからオブジェクトを生成 hero = Player("ヒーロー") # オブジェクトのメソッドを呼び出して状態を変更 hero.take_damage(30) hero.heal(10) hero.take_damage(50) # 別のプレイヤーオブジェクトを作ることもできる slime = Player("スライム") slime.take_damage(10)
この例では、Player
クラスがプレイヤーの名前(name
)と体力(hp
)という状態を持っています。heal
やtake_damage
といったメソッドは、そのオブジェクト自身のhp
を変更します。
関連するデータと操作がクラス内にまとまっているので、グローバル変数を使うよりもずっと整理されていて、コードの管理がしやすくなります。オブジェクト指向の考え方ですが、状態管理の方法として非常に強力です。
【まとめ】Pythonのグローバル変数を正しく理解しよう
さて、Pythonのグローバル変数について、その使い方から注意点、そして代替策まで一通り見てきました!
最後に、大事なポイントをまとめておきましょう。
- グローバル変数 プログラム全体からアクセスできる変数。
- ローカル変数 関数の中だけで使える変数。
- スコープ 変数が有効な範囲のこと。
- 参照 関数内からグローバル変数を読み取るのは簡単。
- 変更 関数内でグローバル変数を変更するには
global
宣言が必要。 - 注意点 乱用するとコードが読みにくく、バグの原因になりやすい。
UnboundLocalError
にも注意。 - 代替策 基本は関数の引数と戻り値を使うのがベスト! 関連データをまとめるならクラスも有効。
グローバル変数は、その手軽さからつい使いたくなるかもしれませんが、多くの場合、より良い設計があります。
「本当にグローバル変数でないとダメか?」と一度立ち止まって考えてみる癖をつけると、より読みやすく、メンテナンスしやすいコードが書けるようになりますよ!
【関連記事】 「Pythonとは?」に答える最初の一歩
0 件のコメント:
コメントを投稿
注: コメントを投稿できるのは、このブログのメンバーだけです。