IT・技術研修ならCTC教育サービス

サイト内検索 企業情報 サイトマップ

研修コース検索

コラム

ゼロから歩くPythonの道

CTC 教育サービス

 [IT研修]注目キーワード   Python  UiPath(RPA)  最新技術動向  Microsoft Azure  Docker  Kubernetes 

第13回 スコープとグローバル宣言を学んでより関数を使いこなそう! (菱沼佑香) 2020年11月

吉政創成 アシスタントの菱沼です。
今回も「いちばんやさしいPython入門教室(大澤 文孝氏著) 」を片手に勉強していきます。

さて、前回はdefを使って「関数」を作る方法と、defで使うreturnとは何か?また、returnとprintの役割の違いについて確認しました。
それさえ知っていればできるのかなと思いきや、どうやらdefで関数を作るときに注意すべき点として「スコープを理解する」必要があるのだそうです。今回はこの注意点について学んでみたいと考えています。

スコープってなんだ。

検索してみたところ、プログラミング全般でスコープと言う言葉は利用されるもののようです。

スコープ 【 scope 】
スコープとは、領域、範囲などの意味を持つ英単語で、プログラミングの分野ではプログラム中で変数名などのシンボルが参照可能な有効範囲のことを指す。
プログラム中で定義された変数や定数、関数などを参照・利用できる有効範囲を表し、多くの言語では変数名などの宣言や定義が記述されている位置によって決定される。(引用:IT用語辞典)

と言うことで、変数や関数が影響する範囲ということだそうですが、スコープとされる種類や分類は言語によるのだと言います。種類や分類ってなんだ...と思いますが、ここでテキストにもどってみます。

-----引用-----
P.110
関数を使うときには、大きな注意点が1つあります。それは、関数内の変数と関数外の変数とで、保存場所が違うと言う点です。

(中略:コードを利用した説明が入っているのですが、とりあえずスコープの意味を確認するため割愛しています。)

P.111、112
こうした変数の有効範囲のことを「スコープ(scope)」と言い、関数の外部のスコープのことを「グローバルスコープ」、関数の内部のスコープのことを「ローカルスコープ」と言います。そしてグローバルスコープに置いた変数のことは「グローバル変数」、ローカルスコープに置いた変数のことは「ローカル変数」と言います。
(中略)
この結果から、次のことがわかります。
①関数の内側(ローカルスコープ)から、関数の外部(グローバルスコープ)の変数を参照することはできる
②ただし関数の内部(ローカルスコープ)から関数の外部(グローバルスコープ)の変数に代入することはできない。そうした場合、ローカルスコープに別の変数が作られる
----------------

文中、関数を利用して解説がされていたので、実際にサンプルコードを使ってやってみました。

> グローバルスコープとローカルスコープのイメージ

fig01

注:もとのサンプルコードには、青枠にあるa="サザエさん"は書かれていません。
(テキストではa="def"を同じの場所に追記してより丁寧に違いの開設がされていましたが、グローバルとローカルを一つで表すため、元のサンプルコードの状態のキャプチャは用意しませんでした。)

画像を見て頂くとわかる通り、defによって新たに作られたtest関数の中に、変数aの値が設定されたことで、test関数自体の結果は「サザエさん」になります。(元のサンプルのまま、test関数内にa="サザエさん"がない状態なら、最初に定義されたa="abc"が入ることになります。)

オレンジ枠のprint(a)は、もともとaという箱の中に入れられた値(abc)を呼び出すものなので、最初に定義されたaの値が表示されます。そのため、オレンジのprint(a)が削除されれば、単純にその分の結果が表示されないだけになります。

というわけで、グローバルスコープとローカルスコープの差は画像のようなイメージで理解しました。

> globalを使えば外にある変数の値を置き換えられる
中と外は別と言うことは理解しましたが、そうは言えど、せっかく作った関数の結果を他でも使えるなら利用したいですし、利用する必要がある場面も出てくると思います。
そうしたときは「global」を使えばいいそうです。

-----引用-----
P.112
とはいえ、関数からグローバルスコープの変数を書き換えたいこともあります。
そのようなときの救済手段として、関数内で「グローバルスコープに置かれた子の変数を使いたい」と書いておくと、その変数だけはグローバルスコープのものに利用できるようになります。
(中略)
追加したのは、黄色いマーカーの「global a」の部分です。このように記述すると、変数aは(ローカルスコープではなく)グローバルスコープを指すようになります。(中略)(globalと書いた行のこと)を、「グローバル宣言」と言います。
----------------

先ほどのサンプルコード(サザエさん追記済み)に、グローバル宣言[ global a ]を追加してみました。

fig02

たしかに、両方の答えが「サザエさん」になりました。この答えに至るまでの流れが左側に記したものです。最初は変数aにabcが入ったものの、test関数が呼び出されると、グローバル宣言によって関数内で設定されている変数aの中身であるサザエさんに入れ替わったということなのだそうです。
そのため、test関数内のprint(a)も、test関数外のprint(a)も、答えはサザエさんとなります。

さて、なんだか便利そうな気がするglobalですが、使う際の注意点はあるのかを調べてみました。

①予期せず変数の値が書き換わってしまうことがある
②無駄にメモリを占有してしまう場合がある
=グローバル変数を使用する場合はきちんと設計した上で使用するべき
(参照元:Pythonでのグローバル(global)変数の宣言方法|UK MILK

確かに、どこで何をどう使うのかを決めておかないと、ごちゃ混ぜになっておかしな原因を調べるのにとっても時間がかかりそう...。いろいろ探していたら躓いた部分をまとめられている方やglobalについて丁寧に説明されているページがありましたのでそちらもご紹介。

打倒、Pythonのグローバル宣言|Qiita @dearmarie
Python global宣言【書き換えの時に宣言が必要な理由】
globalとnonlocalの宣言に関係する疑問点を検証してみました|Snow Tree in June

デフォルト引数で省略できる

前回、defを利用して関数を作ったとき、同時に引数も設定しました。(前回画像引用)

fig03

これはtashizanという関数を新たに作りました→aとbに数字が代入された場合、a~bまでの数字を順番に足していきます→結果をお知らせします、というものでした。
この赤枠がその代入する値を決めたものですが、(1,5)と書かれています。なので、aには1を、bには5が代入されることになりますが、例えばbの値を関数内で先に設定しておくことが可能なのだそうです。
それがこちらの図です。

fig04

関数の定義の際にbは10であることを決めたため、関数の呼び出しの際にはaに代入する値だけを指定することが可能になります。(単純に10だけにした場合には構文エラーが発生しました。)

これ以外に、関数に追加の情報を与えるために「項目名=値」と言うのを追加することもできるそうですが、ややこしくなってしまうため、これは必要になったときに説明されるようです。なので今回は割愛します。

実験の時間

さて毎度おなじみ某国民的ご一家のご長男にご登場いただき、テストの点数によって家族の反応が変わるという良くわからないものを作りました。

まずはグローバル宣言がないものから。

fig05

カツオちゃんに点数改ざん疑惑が発生しました。恐ろしい子・・・。
(注:カツオちゃんに恥をかいて頂いていますが、決してカツオちゃんは悪い子ではありません。)

これはもともとtensuと言う変数に入っているのが0なので、katsutest関数のスコープの外にあるprintはグローバル変数が適用されるためとなります。

さて次に、グローバル宣言をしてみます。

fig06

なんだかどっちにしても改ざん臭が漂っている気がしますが、本当は100点らしいです。
(注:カツオちゃんに恥をかいて頂いていますが、決してカツオちゃんは悪い子ではありません。)

グローバル宣言によって、katsutest関数の結果が出るたびにtensuに値が入っていくため、最後に表示されるのはkatsutest関数が終了したときの値ということになります。
なので、本当に100点なのかもしれません。

それでは今回はここで終了です。今回もお付き合いいただきありがとうございました。

 


 

 [IT研修]注目キーワード   Python  UiPath(RPA)  最新技術動向  Microsoft Azure  Docker  Kubernetes