python3の日記

Python3を図書とともに勉強していくブログ

【初めてのPython(第6章)】変数

f:id:pokita:20180426195830j:plain

変数

Pythonにおいて、変数に値を代入するということは他言語と異なり、その変数自体が代入された値として振る舞うというわけではない。変数はあくまでも代入されたオブジェクトへの参照である。

そのため、他言語では必要となる変数の型宣言が必要ない。

VBでいうところのDim a As String みたいなやつ。)

これをダイナミックな型付けという。決して、Variant型の変数だから何でも型が入るという道理ではない。

 

a = 3 とい代入式には以下の処理が行われている。

 

1.オブジェクト(int型:3)が生成される。

2.変数aが生成される。

3.変数aに1.のオブジェクトのリファレンスが渡される。

 

変数が型情報を持っているわけではなく、変数はあくまでもリファレンス。リファレンス先のオブジェクトが型情報を持っている。

X = "spam"
print(type(X))
#Xのリファレンスから"spam"が呼び出されprint(type("spam"))となり
>>>< class "Str" >

また、変数のリファレンスは必ずオブジェクトであり、変数が変数のリファレンスを持つことはない。引数に渡されたものの識別値を戻すid関数を使ってその働きを見てみる。

a = 2
b = a
print(id(a)) 
>>>1446687520
print(id(b)) 
>>>1446687520
#変数aとbはどちらも同じオブジェクト(int型)2なので、識別値は同じ。
 
a = 2
b = a #①
a = "spam" #②
print(id(a)) 
>>>72180672
print(id(b)) 
>>>1446687520
#①でbが渡されたのは変数a、ではなく変数aのリファレンス先であるオブジェクト(int型)2。変数aは②で新たなオブジェクト(str型)"spam"のリファレンスが渡されるため、aとbの識別値は異なる。

同等と同一

同等とは演算子==がTrueで戻される関係のこと。値が等しいということ。同一とは演算子isがTrueで戻される関係のこと。リファレンスしているオブジェクトが同じ(id関数で戻す値が等しい)ということ。

同等∋同一な感じ。

 

それともう一つ。

 

Pythonではキャッシュの小さいstr型やint型は、同一のオブジェクトがすでに生成されていればそれを再利用するが、その他の型は再利用せず新たな(idが異なる)オブジェクトが生成される

a = 2
b = 2
print(id(a)) #①
>>>1446687520
#①(int型)2と記述したことにより、オブジェクト(int型)2が生成された。このオブジェクトの識別値は1446687520である。
print(id(b)) #②     
>>>1446687520

#②(int型)2と記述よりオブジェクト(int型)2が生成されるところ、①で生成済みなのでリサイクルして使われる。そのため、オブジェクトの識別値は①同様1446687520である。
print(a is b)
>>>True
 
a = [1,2,3] #③
b = [1,2,3] #④
print(id(a))
>>>82790600
print(id(b))
>>>82791640
#③(list型)[1,2,3]の記述により(list型)[オブジェクト[1,2,3]が生成される。(list型)[1,2,3]の記述により(list型)[オブジェクト[1,2,3]が生成される。str型やint型と違い、同一だからといって同じオブジェクと再利用するわけではない。③と④は同等だがそれぞれ別のオブジェクトなため、同一ではない。
print(a is b)
>>>False

mutableとimmutable

Pythonの型には変更可能な型と変更不可能な型がある。前者をmutableな型、後者をimmutableな型と呼ぶ。上書きとはオブジェクトのキャッシュをそのままに(idを変えずに)その内容を変えること。

 

なぜわざわざ英語かというと、Pythonは日本語のコミュニテイがまだ小さいため、そのうち英語のリファレンスなんか読む必要があるため。

 

【変更不可(Immutable)な型】
int, float, str, tuple, bytes, frozenset 等

a = "spam"
a[2]="e"
>>>TypeError: 'str' object does not support item assignment
 

【変更可能(Mutable)な型】
list, dict, set, bytearray 等

a = [0,1,2,3]
print(a,id(a))
>>>[0,1,2,3],55003336
a[1] = 2
print(id(a))
>>>[0,2,2,3],55003336
#オブジェクト[0,2,2,3]は生成された、のではなく、すでにあるオブジェクト[0,1,2,3]の上書きによってできたもの。ゆえにidは同等。
b = a
a.append(4)
print(b,a is b)
>>>[0, 2, 2, 3, 4] True
#変数a,bは共に同じオブジェクト[0,2,2,3]を参照している。a.append(4)により、オブジェクト[0,2,2,3](id55003336)が[0,2,2,3,4]に上書きされた。変数bはid:55003336のオブジェクトへのリファレンスなので、bはa.append(4)による上書き後のオブジェクトを返す。

値渡しと参照渡し

 それぞれ独立した名前空間の間を変数の値が行き交うときに問題になるのが値渡しと参照渡しである。

 

【値渡し】行き来する変数がmutableな型のとき起こる。変数はどの名前空間においても同一idのものとなり、渡された名前空間で加えられた変化が、元の名前空間でも反映された形となる。

def foo(a):
  print(a,id(a))
  a.append(1)
  print(a,id(a))


b = [0]
#オブジェクト[0](id:74795208)が生成された     
print(b,id(b))
>>>[0] 74795208 
foo(b)      
>>>[0] 74795208
>>>[0, 1] 74795208
#変数bは関数fooに渡されることでfoo内においてa = b となりaにオブジェクト[0](id:74795208)が
代入される。

print(b,id(b))    
>>>[0, 1] 74795208 
#関数fooの実行によりbのリファレンス先のオブジェクト(id:74795208)は更新された。

【参照渡し】行き来する変数がimmutableな型のとき起こる。渡した先で変数に変更が加えられた際は、その瞬間新しいオブジェクト(渡したオブジェクトととidが異なる)が生成される。idが違うことから渡し先の名前空間の処理が終わった際に戻す変数のリファレンスと、元から存在する変数のリファレンスは一致せず、よって、元から存在する変数のリファレンスが残る形になる。

def foo(a):
  print(a,id(a))
  a.append(1)
  print(a,id(a))


b = 0     
#オブジェクト0(id:74795208)が生成された
print(b,id(b))
>>>0,74795208 
foo(b)      
>>>0,74795208
>>>1,74795311
#変数bは関数fooに渡されることでfoo内においてa = b となりaにオブジェクト0(id:74795208)が代入される。+1されたことで新オブジェクト生成される。

print(b,id(b))  
>>>0,74795208 
#bのリファレンス先はimmutableなので不変故に値は変わらず、参照先も変わらない。