※ 他の方の記事のコメントに書いたのですが、みなさんにも知って欲しくて記事にしました。
オブジェクト(値)と変数
Pythonでは、None、数値、関数、クラス、メソッド、モジュールなど すべて が オブジェクト(何らかのクラスのインスタンス)に 変換 されます。
変数 は オブジェクトへの参照値 である オブジェクトid を 保持 します。どんなオブジェクトでも 代入 できます。違う型のオブジェクトを 再代入 することもできます。
変数 は、変数名 を 辞書キー として、オブジェクトid を 辞書値 として 変数辞書 に格納されます。変数辞書 の内容は、vars
関数, locals
関数, globals
関数で確認できます。
リスト は オブジェクトid の 配列 です。配列要素毎に違う型のオブジェクトを 代入・再代入 することもできます。
変数代入・再代入
>>> id(1)
15812500000
>>> type(1)
<class'int'>
>>> 1 .__class__ # 小数点に解釈されないようにドットの前に空白を入れる
<class'int'>
>>> 1 .real
1
>>> id(2)
15812500032
>>> a = 1
>>> id(a)
15812500000
>>> a = 2
>>> id(a)
15812500032
>>> vars()
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <class'_frozen_importlib.BuiltinImporter'>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>,
'a': 2}
変数への累算代入その1:イミュータブルオブジェクトの場合
+=
のような 代入 を 累算代入 といいます。
Pythonの累算代入は、変数に代入されているオブジェクトが イミュータブル(不変)オブジェクト か ミュータブル(可変)オブジェクト かで動作が違います。
int型のようなイミュータブルオブジェクトの場合は、下図のように別オブジェクトを再代入します。
>>> a = 2
>>> id(a)
15812500032
>>> a += 1
>>> id(a)
15812500064
リストオブジェクト
>>> id(1)
15812500000
>>> id(2)
15812500032
>>> id(3)
15812500064
>>> a = [1, 2, 3]
>>> id(a)
123145300973832
>>> type(a)
<class'list'>
>>> a.__class__
<class'list'>
>>> len(a)
3
>>> a.__len__()
3
>>> for ele in a:
... print(ele, type(ele), id(ele))
...
1 <class'int'> 15812500000
2 <class'int'> 15812500032
3 <class'int'> 15812500064
>>> vars()
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <class'_frozen_importlib.BuiltinImporter'>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>,
'a': [1, 2, 3],
'ele': 3}
オブジェクトへの累算代入
>>> a = [1, 2, 3]
>>> id(a[0])
15812500000
>>> a[0] += 1
>>> id(a[0])
15812500032
>>> id(a[1])
15812500032
なお、-5
~ 256
の整数値(int型オブジェクト)はよく利用される値としてオブジェクトがキャッシュされて 共有再利用 されるため、a[0]
と a[1]
の オブジェクトid が同じになっています。
オブジェクト指向のデザインパターンでいう「flyweightパターン」が使われています。
変数への累算代入その2:ミュータブルオブジェクトの場合
リストはミュータブルオブジェクトのため、int型オブジェクトのときとは動作が異なります。
下図のようにオブジェクトに処理を依頼し、オブジェクト自身がオブジェクト内容を変更します。
>>> a = [1, 2, 3]
>>> id(a)
123145300973832
>>> hasattr(a, '__iadd__')
True
>>> hasattr(2, '__iadd__')
False
>>> a += [4]
>>> id(a)
123145300973832
>>> len(a)
4
>>> a.__len__()
4
>>> id(a[3])
15812500096
参考
累算代入文: https://docs.python.org/ja/3/reference/simple_stmts.html#augmented-assignment-statements
累算代入メソッド: https://docs.python.org/ja/3/reference/datamodel.html#object.__iadd__