Python3 – オブジェクトへの参照

概要

Pythonで関数の受け渡しが参照渡しとされているが、改めて変数とオブジェクトの関係を含めて確認してみた。

オブジェクトの参照に関する基本的な考え方は

  • 論理値や数値も含めて全てオブジェクト
  • 変数にはインスタンスのアドレスが格納され、参照される
  • 数値や文字列などイミュータブルなオブジェクトの場合、リテラルの表現が同じものは共通の1つのインスタンスとなる
  • リストなどミュータブルなオブジェクトは、インスタンスの内容が変更されても参照先は変わらない(逆に言えば、参照先が変わらないのに内容が変更されている可能性がある)

関数の引数の受け渡しは

  • 仮引数は、関数が呼び出し時には元のオブジェクトへの参照を指しているが、イミュータブルオブジェクトが変更された場合は参照先が変わり、呼び出し元の引数に影響を与えない
  • 引数の参照先がミュータブルオブジェクトの場合、インスタンスの内容が変更されても参照先は変わらず、呼び出し元の内容も変更される

変数からオブジェクトへの参照

数値の場合

下記のコードをまず確認する。

ある数値を変数に代入すると、その変数には数値(オブジェクト)のアドレスがセットされる。その変数の内容(アドレス)を別の変数に代入すると、新しい変数も同じアドレスをさすようになる。

次に、同じ数値を指している変数の一つに別の数値を代入すると、その変数は新しい数値オブジェクトのアドレスを指すようになる。

以下のように、同じ値の数値は1つのオブジェクトのアドレスが共有される。

数値計算の場合も、結果が同じ値なら1のオブジェクトが共有される。

変数が絡む演算でも、結果が同じ値なら同じアドレスを指す。

浮動小数点の場合、精度上少しでも異なる値は違うオブジェクトになる。

文字列の場合

数値の場合と同じで、変数は文字列オブジェクトのアドレスを指す。

異なる内容の文字列は、異なるオブジェクトとなる。

同じ内容の文字列リテラルは、異なる位置で用いられても1つのオブジェクトとして共有される。

面白いことに、文字列リテラル同士の演算結果が同じなら、これも同じオブジェクトとして共有される。

同じオブジェクトを指していても、一方に演算を施すと新たなオブジェクトが生成されるため、異なるオブジェクトを指すようになる。

なお、リテラル同士の演算で結果が同じ場合はオブジェクトが共有されたが、変数が絡む場合は、内容が同じであっても異なるオブジェクトとなる。

リストの場合

リストの場合もオブジェクトへの参照が変数に保存される。

リストの要素を変更した場合、そのリストを指している全ての変数に変更結果が反映される。

注意点。リストの場合、リテラルが同じでも異なるオブジェクトが生成される。

オブジェクトが異なるため、片方の変更は他方に反映されない。

関数の引数の参照

数値の場合

関数の引数に数値を渡す場合の流れは以下の通り。

  1. 仮引数は引数と同じオブジェクトを指す
  2. 関数内で引数が変更されると、新たなオブジェクトを指すようになる
  3. その結果、仮引数に渡した変数は変更されない

文字列の場合

文字列の場合も、関数内での変更は呼び出し元に影響を与えない。

リストの場合

リストを引数に渡した場合、関数内での変更が呼び出し元にも影響を与える。リストのようなミュータブルオブジェクトの場合、それに対する変更は元のインスタンスに対する変更であり、イミュータブルなオブジェクトのように新しいインスタンスが生成されるわけではないため。

元のインスタンスに影響を波及させたくない場合はコピー、ディープコピーを使う。

 

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です