やあ、シミヅです!
最近、僕の会社では大規模なニューラルネットワークの学習パイプラインを組んでいるんですけど、設定値をどこからでも参照できるようにしたくてグローバル変数をいじっていたら、思わぬところでバグを出してしまって……(笑)。
「えっ、値を変えたはずなのに反映されてないっ!?」
「関数の中で代入しようとしたらエラーが出たんだけどっ!?」
Pythonを触り始めたばかりの頃、僕もこの「グローバル変数の代入」には何度も泣かされました。
実はPythonには、他の言語とはちょっと違う「スコープ(変数の有効範囲)」の独特なルールがあるんですよ。
というわけで今回は、Pythonでグローバル変数に正しく値を代入する方法について、AIエンジニアの僕と一緒に深掘りしていきましょうっ!
執筆者:シミヅ |
|
結論!関数内で代入するなら「global」宣言が必須
Pythonでは、関数の「外」で定義した変数を関数の中で「読む」だけなら、実は何も特別なことはしなくていいんです。
でも、その変数に「代入(書き換え)」をしようとした瞬間に、Pythonがヘソを曲げちゃうんですよ。
まずは、失敗するパターンと成功するパターンを見比べてみましょう!
# グローバル変数
count = 0
def failed_update():
# global宣言を忘れると...
count = count + 1 # ここでエラーになるか、意図しない挙動になりますっ
print(f"関数内:{count}")
def successful_update():
# これが正解っ!
global count
count = count + 1
print(f"成功したよっ:{count}")
このコード、何が起きているか分かりますか?
Pythonのルールでは、関数の中で変数に値を代入しようとすると、Pythonは「あ、これはこの関数の中だけで使う『ローカル変数』を新しく作ったんだな」と勝手に判断しちゃうんです。
だから、failed_update の中では、外側にある count を無視して、新しい count を作ろうとします。
でも右辺の count + 1 で「まだ存在しないローカル変数」を参照しようとするから、「UnboundLocalError」というエラーが出て止まっちゃうんですよ……。
そこで救世主となるのが global count という一文!
これを書くことで、「今からこの関数の中で使う count は、外にあるグローバル変数のことだよっ!」とPythonに教えてあげられるんです。
実際に動かして「スコープ」の違いを体感しよう
理屈だけだと「?」ってなるかもしれないので、実際に動くコードで挙動を確認してみましょう!
message = "初期状態"
def change_message():
global message
message = "シミヅが書き換えたよっ!"
print("関数の中で書き換え完了っ!")
print(f"実行前:{message}")
change_message()
print(f"実行後:{message}")
このコードを実行すると、結果はこうなります。
実行前:初期状態
関数の中で書き換え完了っ!
実行後:シミヅが書き換えたよっ!
解説しますねっ!
最初に message というグローバル変数を作ります。
change_message 関数の中で global message と宣言します。
これによって、関数内の message = ... という代入が、外側の変数に対して行われるようになります。
もしここで global 宣言を抜いてしまうと、関数の中だけで message という変数が新しく作られるだけで、外側の message は「初期状態」のまま変わらないんですよ。
これ、デバッグで見つけるのが本当に大変なバグになるので注意してくださいねっ!
リストや辞書の場合は「global」がいらない!?
ここ、実は中級者でも「あれっ?」ってなるポイントなんですけど、リストや辞書の中身を書き換える時は、実は global 宣言がいらない場合があるんですよ!
ちょっとトリッキーなコードを見てみましょう。
data_list = [10, 20, 30]
def update_list():
# global宣言なしでリストの中身をいじっちゃうよ!
data_list[0] = 999
update_list()
print(f"リストの中身:{data_list}") # [999, 20, 30] になってるっ!
「えっ、代入してるのに global はいらないのっ!?」って思いますよね(笑)。
実はこれ、Pythonの「代入」の定義に関係があります。
data_list = ... のように変数そのものを置き換えるのは「代入」なので global が必要ですが、data_list[0] = ... のように「リストというオブジェクトの要素」を書き換えるのは、Python的には「代入」ではなく「ミュータブル(変更可能)な操作」として扱われるんです。
でも、混乱を避けるために「グローバルなものをいじってるんだぞっ!」という意思表示として、僕はあえて global を書くこともあります。
その方が親切ですからね!
AIエンジニアの僕が教える「グローバル変数の乱用はダメ!」な理由
ここまで書き方を解説してきましたが、最後にとっても大事な話をします。
実務、特に僕がいるAI開発のような複雑な現場では、グローバル変数の多用は「絶対禁止っ!」とされることが多いんです。
なぜかというと、プログラムのどこで値が変わったのかが追えなくなる「スパゲッティコード」の原因になるからなんです。
グローバル変数を使いたくなったら、まずは以下の方法を検討すべきだと僕は思いますっ!
- クラス(Class)を使う:状態をインスタンス変数として管理する。
- 引数と戻り値を使う:関数に値を渡して、計算結果を返してもらう。これが一番綺麗ですっ。
- 設定ファイル(Config)にまとめる:定数として扱い、基本は書き換えない。
エンジニアの世界は「動けばいい」だけじゃなくて、「後で読みやすいか」が命!
常に効率的で美しい設計を目指すのが、プロへの近道ですよっ!
まとめ。正しい知識でPythonをもっと楽しもう!
Pythonのグローバル変数への代入について、スッキリ解決できましたか?
関数内で代入するなら global 変数名 が必須っ!
宣言を忘れると「UnboundLocalError」で怒られる可能性がある!
リストや辞書の「中身」だけ変えるなら宣言なしでも動くけど、注意が必要!
そもそもグローバル変数は使いすぎないのが「デキるエンジニア」の流儀!
プログラミングって、こういう細かいルールの積み重ねなんですよね。
最初は「面倒だなっ!」って思うかもしれないけど、理解できるとパズルみたいに楽しくなってきますよっ。
以上、シミヅでしたっ!またねっ!



コメント