PythonのList型について諸々-Tips for List type in Python

はじめまして、胡と申します。
普段は言語処理の研究開発に携わっており、Pythonで実験環境を作ったりすることが多いです。
今回は、私が実際に実験でよく使うList型に関する操作メモをシェアしたいと思います。

あれ?PythonのListは思った通りに動かないな!

「ヤバイ、backup用のListも上書きされてしまいます!」と、
自分の甘さに後悔したことがあります。

この前提で、foo[0]に'd'を与えると、foo_backも['d', 'b', 'c']になってしまいますよね。
これは、Pythonでは、ある変数にList型の変数をassignment(foo_back = foo)するというのは、
単に“=”オペレータ前の変数(foo_back)に
「その後ろの変数(foo)が参照しているlistを参照してください」
と教えただけだからです。
つまり、上述の例では、foo_back = fooによるList中身のコピーができません。

これを解決するためには、

を使います。
また、以下のコードによるList型データのコピーも可能です。

Shallow copies of dictionaries can be made using dict.copy(), and of lists by assigning a slice of the entire list, for example, copied_list = original_list[:].

--Python 2.7.9 documentation: copy

ここで“foo[:]”というのは、元々はfoo[n:m]であり、
fooの n から m-1 までの要素をスライスするという意味になります。
全Listのコピーであるため、 n と m はそれぞれ 0 と len(foo) になり、
省略することができます。

このように、Listをコピーすることにより、
fooをどのように変更しても、foo_backへの影響はなくなります。

ただし、上述の方法はいずれも浅いコピー(shallow copy)であり、
[['a', 'b'], 'c', 'd'] のような、nested listになったデータのコピーには対応できません。

この場合は、深いコピー(deep copy)を使いましょう。

参考:Python 2.7.9 documentation: copy

ほかのスクリプト言語と比較するために、JavaScriptとRubyのListコピー法についてもまとめてみました。

Python、JavaScriptとRubyのListコピー法

shallow copy deep copy
Python foo_back = copy.copy(foo)
  或いは
foo_back = foo[:]
foo_back = copy.deepcopy(foo)
JavaScript var foo_back = jQuery.extend({}, foo);(*)
  或いは
var foo_back = foo.slice(0, foo.length);
  或いは
var foo_back = [].concat(foo);
var foo_back = jQuery.extend(true, {}, foo);(*)
Ruby foo_back = foo.clone
  或いは
foo_back = foo[0..foo.length]
foo_back = Marshal.load(Marshal.dump(foo))

*jQuery使用。また、取得したfoo_backは辞書型Listになります。
 例えばfoo = ["a", "b", "c"]はfoo_back = {0: "a", 1: "b", 2: "c"}になります。
 この場合、foo == foo_backはfalseになってしまいますが、
 foo[0] == foo_backはtrueであり、つまり要素の取得には支障がありません。

nested listを展開

[('a', 'b', 1), ('c', 'd', 1)]のようなTuple型同士(もしくはList型同士)のnested listを
['a', 'b', 1, 'c', 'd', 1]のような1次元Listに展開するためには、例えば以下のような方法があります。

ですが、現実の世界ではやはりより複雑な構造があります。
自分の場合、テキストのツリー構造を展開して単語リストに変換することがしばしばあります。
例えば、「貴社の記者が汽車で帰社した」の構文解析結果の一例を以下に示します。

貴社の┐
  記者が
   |
  汽車で┐
   帰社した

このツリー構造に対して、単語と単語の関係をTuple型、文節と文節の関係をList型で表現すると、例文を
[[[[(u"貴社", u"の")], (u"記者", u"が")], (u"汽車", u"で")], (u"帰社", u"し", u"た")]
に変換することができます。

このListから、例えば単語頻度などの情報を得るために、
複雑なnested list構造(多次元)を単語List(1次元)に直す必要があります。
また、上述のような複雑なnested list構造を1次元Listに変換するためには、
Python2.x系では、compiler packageというものがあります。

しかし、Python 3.x系になるとcompiler packageがなくなってしまいます。

Deprecated since version 2.6: The compiler package has been removed in Python 3.

--Python 2.7.9 documentation: Python compiler package

この場合は、自分でflatten関数を作らなければなりません。

このように展開用の関数を作っておけば、fooの展開が容易にできるようになります。

このflatten関数では、再帰的に展開対象nested listにある要素を判断し、
List型もしくはTuple型の場合はそれをさらに展開し、
そうでない場合はそれを出力Listに追加するという仕組みになります。

Comments are closed, but you can leave a trackback: Trackback URL.