#python #関数内 #関数外 #変数 #共有 #オブジェクト #メンバ変数 #グローバル変数 #global #モジュール
この記事では、pythonにおいて、関数の内と外で変数を共有する方法について、わかりやすく解説します。
本記事を読むにあたって
本記事内のプログラムを実践するには、下記の記事に従って環境を準備してください。
・ 【第2回】pythonのプログラミング環境を作る
関数内の変数の操作は関数内でのみ有効
pythonに限らず他のプログラミング言語では、関数や メソッド の中で使用する変数は、そのままでは、関数の中だけで有効となり、外からはアクセスできません。
また、関数や メソッド の外で使用する変数は、そのままでは関数の中からアクセスできません。
以下の例を御覧ください。
<share.py>
test = "変更していません"
def overwrite() :
test = "変更されています!" # ⬛ここで上書き
print("関数の中では" + test)
overwrite() # ここで関数を呼び出し
print("関数の外では" + test) # ⚫
関数の呼び出しを行ったときに、関数内の⬛のところで変数testを “変更されています!”に上書きしているため、関数から戻って⚫のところで、”変更されています!”になりそうですが、
実行すると予想に反して以下のようになります。
(base) PS D:\blog\global> python .\share.py
関数の中では変更されています!
関数の外では変更していません
つまり、関数の外と中で同じ名前の変数を使用しても、それぞれが独立した変数として扱われます。
なぜそうなるのか
関数は、下記のように、入力(一般に引数と呼ぶ)に変数を指定して呼び出すことができます。
下記の例では、X1, X2が入力に指定した変数です。
<share.py>
def hoge(A, B) :
A = 1000
B = 2000
X1 = 10
X2 = 20
hoge( X1, X2 )
しかし、関数hogeには、変数という容れ物が渡されるのではなく、変数内に格納されている値が渡されます。
そのため、関数の中では、変数そのものを直接アクセスすることはできません。
上の例では、hoge関数の中では変数はA, Bになり、X1, X2ではなくなっているため、アクセスできなくなることは明白です。
仮に”def hoge(X1, X2) :”と記載されていても、このX1,X2は、外のX1,X2とは別物になり、やはりアクセスできません。
このように、関数は入力を通して外部の値を受け取ることはできても、変数そのものを受け取ることができないようになっています。
同様に、関数の中で使った変数を、外からアクセスすることができないようになっています。
関数の内と外で同じ変数にアクセスするには
前述のとおり、基本的には関数の内と外では同じ変数をアクセスすることができないのですが、少し手を加えて、同等のことを可能に、あるいは強制的に可能にする方法があります。
以下の3つのやり方があります。
1.オブジェクトを介してメンバ変数としてアクセスする
2.グローバル変数として宣言する
3.グローバル変数として宣言したあとモジュール化する
基本は後述しますが、関数の仕組みに従った1.を推奨します。
3.についても2.に比べると推奨できます。
2.はあまりお勧めしませんが、限定的に使用しても良いと思われます。
それぞれ、考えて選択しましょう。
オブジェクトを介してメンバ変数としてアクセスする
関数の入力に オブジェクト を渡し、オブジェクト内の メンバ変数 として関数の内と外のどちらからもアクセスできるようにします。
<share.py>
class message :
test = "変更していません"
def overwrite(mes) :
mes.test = "変更されています!" # ⬛ここで上書き
print("関数の中では" + mes.test)
m = message() # クラスを具現化(インスタンス化)してオブジェクトに
overwrite(m) # ここで関数を呼び出し
print("関数の外では" + m.test) # ⚫
上の例では、messageという クラス を定義し、そのメンバ変数として testを定義しています。
そして、”m=message()”で オブジェクト として具現化(インスタンス化)し、関数の入力にオブジェクトを指定して関数を呼び出しています。
関数内では、オブジェクトは変数mesとして受け取られます。
⬛のところで、mes内のtestを上書きすると、関数から戻った⚫のところで変更した値を参照することができます。
実行すると以下のようになります。
(base) PS D:\blog\global> python .\share.py
関数の中では変更されています!
関数の外では変更されています!
オブジェクト指向言語 としての使い方にも自然に馴染んでおり、関数の基本的な用法を曲げていないため、最もお勧めする方法です。
グローバル変数として宣言する
関数外の変数と同じ名前の変数をglobalキーワードを用いて グローバル変数 として宣言することで、関数の内と外でアクセスすることができます。
下記の例では、★のところでglobal宣言しています。
<share.py>
test = "変更していません"
def overwrite() :
# 例えばjavaでは、ここに以下の変数宣言が入ります
# String test;
global test # ★globalとして宣言
test = "変更されています!" # ⬛ここで上書き
print("関数の中では" + test)
overwrite() # ここで関数を呼び出し
print("関数の外では" + test) # ⚫
実行すると以下のようになります。
(base) PS D:\blog\global> python .\share.py
関数の中では変更されています!
関数の外では変更されています!
ただし、グローバル変数として定義する方法は、簡単ですが推奨できません。
理由は、関数の中でアクセスされている可能性に気づきにくいケースがあるためです。
上記の例では、小さなファイル内に関数定義されていますが、何百行もあるプログラムにまで成長してしまうと、関数の中でアクセスされているかどうかを、いちいちスクロールして見に行く必要があり、間違いも発生しやすくなります。
小さなファイルである間はこの方法にしておき、プログラム行数が大きくなってきた場合は、この後のモジュール化と組合せる方法か、他の方法に移行するのが良いでしょう。
グローバル変数として宣言したあとモジュール化する
グローバル変数として宣言した上で、 モジュール 化してしまうことで、単にグローバル変数を宣言した場合に比べて問題を改善するのがこの方法です。
pythonでは、モジュール化は別のファイルに追い出しをすることで可能です。
以下に例を示します。
<func.py>
def overwrite() :
global test # ★globalとして宣言
test = "変更されています!" # ⬛ここで上書き
print("func.testは関数の中では" + test)
<share.py>
import func
#test = "変更していません"
func.test = "func.testは変更していません"
func.overwrite() # ここで関数を呼び出し
#print("関数の外では" + test) # ⚫
print("func.testは関数の外では" + func.test) # ⚫
実行すると以下のようになります。
(base) PS D:\blog\global> python .\share.py
func.testは関数の中では変更されています!
func.testは関数の外では変更されています!
モジュール化することで、変数の先頭にモジュール名か、”import ~ as ~”のasで変更した変更名(エイリアス)を付ける必要があるため、関数内でアクセスされることが明確になります。
ただし、モジュール内においては、”グローバル変数として宣言する”で見られる問題が残ります。
上記の例のfunc.pyでは、変数 testを関数の外で使用していませんが、使用した上でfunc.pyのプログラム行数が大きくなってしまった場合に、やはり問題が浮上してしまいます。
最後に
本記事では、 関数の内と外で変数を共有する方法について解説しました。
1.オブジェクトを介してメンバ変数としてアクセスする
2.グローバル変数として宣言する
3.グローバル変数として宣言したあとモジュール化する
1⇒3⇒2 の順にお薦めです。
次回は別の基本について解説しますので、ご期待ください!
この記事へのお問い合わせや、無料のサポート、一歩踏み込んだ有料サポートが必要な場合は、ホームページからメールで受け付けています。お気軽にご連絡ください。
コメント