のーずいだんぷ

主に自分用メモですが、もしかしたら誰かの役に立つかもしれません

Pythonで新しいリストオブジェクトを作る方法(Copyメソッド)

一日一Python!はじめました!

最近業務でPythonに対する理解が以前と変わらないままだな…と思って始めた。 プログラミング言語の仕様を一度に把握するのは大変だけど、毎日一つずつ覚えていったらいつの間にかかなりマニアックな仕様すらも簡単に理解できるのでは?という取り組み。

Pythonのリファレンスや、参考書を読んでこれは知らなかったなとか、うろ覚えだな…というものを個人的に紹介していく。 Python初心者の方の役にたてば嬉しい。

教材としてしばらく以下の本を使用する。

入門 Python 3

入門 Python 3

この本、入門とあるが意外と侮れなくて自分はそれなりにPythonの基礎は理解しているつもりだったのだが役に立ちそうな部分がたくさんあった。 多くの人が業務に関わる部分で偏った使い方をするので何かしらの穴はきっとあるはず、少なくとも自分はそうだった。

かなり分厚いので驚くが、目次を見ると自分に足りない部分は一目瞭然なので案外ぱっと読める。 なお、そろそろ原著は第2版が出るそうなので、英語に抵抗ない方はこちらを購入したほうがいいだろう(私は予約した。)

Introducing Python: Modern Computing in Simple Packages

Introducing Python: Modern Computing in Simple Packages

今日のお題は

「リストのディープコピー」 です。

Pythonのリストの特徴

本題の関わる前提として、Pythonでリストを使わない人はいないと思うが、例えば以下のように変数にリストを作成できるのはよく知られているだろう。 以下のコードのように別のリストへコピーすることもできる。

>>> li = ["a", "b", "c"]
>>> li_copy = li
>>> print(li)
['a', 'b', 'c']
>>> print(li_copy)
['a', 'b', 'c']

問題はここからで、もしliの値を変えるとどうなるだろうか

>>> li[0] = "A"
>>> print(li)
['A', 'b', 'c']
>>> print(li_copy)
['A', 'b', 'c']
値の変更が連動している!

これは説明が難しいのだが、「共有渡し」という状態でPythonでは全ての値が共有されている(一つの変数に別名がついているイメージ) 実際はプリミティブ型はイミュータブルなので変更されず新しいオブジェクトが生成されるのだが、リストの各様子はミュータブルなのでこのような挙動となる。

リストで新しいオブジェクトを生成する方法は?

以下のいずれかの方法で可能だ。(私は3の方法を知らなかった)

  1. list()
  2. スライス
  3. Copy()

1 list()

list()は文字列からリストを作ったりするときにも使用するが、リストを引数に渡すとコピーを作成する。

>>> li_deep1 = list(li)
>>> print(li_deep1)
['A', 'b', 'c']
>>> li_deep1[0]=1
>>> print(li)
['A', 'b', 'c']
>>> print(li_deep1)
[1, 'b', 'c']

2 スライス

スライスはリストオブジェクトの末尾に[<start>:<stop>:<step>]を指定することで配列から要素を抜き出したリストを作成することができるが、 値を指定しないと、以下のようにリストまるごとコピーしたことになる。

>>> li_slice = li[:]
>>> li_slice[0]=2
>>> print(li)
['A', 'b', 'c']
>>> print(li_slice)
[2, 'b', 'c']

3 Copyメソッド

これは通常のオブジェクトのメソッドライクに<コピーしたいリスト>.copy()でリストのコピーが返り値として得られる。

>>> li_copy = li.copy()
>>> li_copy=3
>>> print(li)
['A', 'b', 'c']
>>> print(li_copy)
3

厳密にはどう違うか気になる… また一つPythonistaに近づけたかな?