Python勉強記(9) リストと沼
リスト(list)とは?
Pythonにおいて、リストは動的配列として実装されています。
複雑なことに、リストは組み込みのデータ型でありながら、標準ライブラリのarrayでも定義できます。両方ともtype がlistであることも確認できます。
a = [1, 2, 3, 4, 5] # 組み込みList型
print(a)
>> [1, 2, 3, 4, 5]
print(type(a))
>> <class 'list'>
import array as ary # 標準ライブラリ array のList
ary.a = [1, 2, 3, 4, 5]
print(ary.a)
>> [1, 2, 3, 4, 5]
print(type(ary.a)) # こちらもlist型。
>> <class 'list'>
print(a==ary.a) # a==ary.a ?
>> True
簡単な使い方
Pythonの一般的な教科書では、リスト、タプルの順に記述されますが、私はタプルを先に調べました。
リストの沼に踏み込む前に、簡単な使い方を抑えておきます。最初は
リストはタプルの書き込み可能バージョンである。
と理解していてOK。
リストの一番易しい使い方は一次元配列です。
リストを利用して(もうかなり飽きてきたが)フィボナッチ数列の改良版をつくってみます。
def fib2(n):
fib_list = [] # リストを初期化する
a, b = 0, 1
while (a < n):
fib_list.append(a) # 数列をリストに追加する。
a, b = b, a + b
return fib_list
k = int(input())
print(fib2(k))
前回作ったfib() は、関数の中でprint(a)しているのがいまいちでした。
上記のfib2()は、整数n を受け取り n を超えない範囲のフィボナッチ数列のリストを返します。漸化式の計算は同じですが、fib_listというリストを作成し、fib_list.append(a)のところで、次々と項を追加しながら計算を進めます。
また、プログラム内でどこまで計算するか、fib(1000)とか書いていた分をinput() でキーボードから入力できるようにしました。最後のprint(fib2(k))でリストに格納した数列を一気に出力します。
Input: 20
>> [0, 1, 1, 2, 3, 5, 8]
操作方法
リストはミュータブル(書き換え可能)なシーケンス型です。
そのためタプルより自由度が大きく、要素の追加、削除、ソート・・・など自由自在です。
下記でlist.append(x)はリスト末尾に要素xを追加する。list.pop()はリスト末尾から1個要素を削除(削除した値をreturnする)メソッドです。
(メソッドについては下記)
a = ['a','b','c','d','e']
a.append('f') # 'f' を追加する
print(a)
>> ['a', 'b', 'c', 'd', 'e', 'f']
a.pop() # 最終文字を取り除く
print(a)
>> ['a', 'b', 'c', 'd', 'e']
a[1]='x' # a[1} を'x' に書き換える。
print(a)
>> ['a', 'x', 'c', 'd', 'e']
del a[1] # a[1] を消去する。
print(a)
>> ['a', 'c', 'd', 'e']
print a[1} # a[1]の値を確認する。
>> ['c']
リストのソート(メソッドと関数の違いも)
リストをソートするには、2種類方法があります。
リストそのものをソートしたい。
元リストは壊さずに、ソートした新しいリストを得たい。
1番目をlist.sort() メソッドを使ってやってみます。(逆順ソートするには引数reverse=True を与えます)
a = ['a','b','c','d','e']
a.sort(reverse=True) # 逆順にソート
print(a)
>> ['e', 'd', 'c', 'b', 'a']
a.sort() #昇順にソート(もとに戻す)
print(a)
>> ['a', 'b', 'c', 'd', 'e']
2番目は組み込みのsorted() 関数を使います。sorted()にリストを渡し、結果をbに受け取る方法です。
※この方法でタプルのソート結果を得ることが可能です。
a = ['a','b','c','d','e']
b = sorted(a,reverse=True) # a のソート結果を返す(aは壊さない)
print(b)
>> ['e', 'd', 'c', 'b', 'a']
print(a)
>> ['a', 'b', 'c', 'd', 'e'] # aは壊していないのでそのまま。
a = ('a','b','c','d','e') # タプルをソートしたいとき
b = sorted(a,reverse=True) # a がタプルでも問題ない。
print(b)
>> ['e', 'd', 'c', 'b', 'a'] # 結果はリスト。タプルが欲しければ変換する。
b=tuple(b)
print(b)
>> ('e', 'd', 'c', 'b', 'a')
どちらを使うかは場合によりけりですが、ここで重要なことを学びました。メソッドと関数の違いです。
リストの沼とは
データはすべてオブジェクト
Pythonプログラムでのデータは全てオブジェクトです。
公式ドキュメントに、オブジェクトとは何ぞや?が書かれていますが、オブジェクト指向言語に慣れている人には自明ですが、我々初学者にはすっと頭に入ってきません。
重要なところを引用します。
今回話題にするのは同一性です
同一性はid関数またはis演算子を使用して調べられます。
以下のいずれかの時、オブジェクトx, yは同一です。
id(x) == id(y)である。
x is y がTrueである。
オブジェクトの代入がどのような結果になるか確認します。
print(id(1))
>> 140708457609656 # オブジェクト1 のid
x = 1
y = x
print(id(1), id(x), id(y))
>> 140708457609656 140708457609656 140708457609656 # 1,x,y は同一なオブジェクト
print(x is y is 1)
>> True # Warningが出るかもしれません。
上記の代入により、同一なオブジェクト1, x. yができました。言い換えると変数x, y はオブジェクト1を参照しています。公式ドキュメントによるとid(x)はxのメモリ上のアドレスであると書かれています。
上の続きで、x += 1 とするとどうなるでしょう?
x += 1
print(id(1), id(x), id(y))
>> 140708457609656 140708457609688 140708457609656
print(id(2))
>> 140708457609688
id(x) の値のみ変化し、id(2)と等しくなりました!
つまりx は2を参照するようになりました。yは触ってないので1を参照したままです。
次にリストの代入を試してみます。
x = [1, 2, 3]
y = x
print(x, y)
>> [1, 2, 3] [1, 2, 3]
x[0] = 99
print(x, y)
>> [99, 2, 3] [99, 2, 3] # y の方も変化した?
xの要素だけ触ったのに、y の方も変わったのは不思議だと思いませんか?
そのまえの代入y = x において、xとy は同じオブジェクトを参照しているため、x の要素を変えたら連動してyの値も変わる。よく考えたら当たり前ですが、ブログラムの現場ではハマりそうな沼だと思いました。
ハマらないように注意しないと・・・。
この記事が参加している募集
この記事が気に入ったらサポートをしてみませんか?