今さらPython3 (14) - リストにまつわるその他もろもろ

この本の3章、リストのあたりを読んでます。

入門 Python 3

入門 Python 3

マルクス兄弟の話は続きます。

いるの?いないの?

>>> marxes = ['Groucho', 'Chiro', 'Harpo', 'Zeppo']
>>> marxes.index('Chiro')
1
>>> 'Groucho' in marxes
True
>>> 'Bob' in marxes
False

試してないけど、Chiroさんが2人いる場合は、最初の方のインデックスを返すんだよね。inを使って要素の有無を調べるのは便利。

>>> words = ['a', 'dear', 'a', 'female', 'deer']
>>> 'deer' in words
True

ドレミの歌の冒頭かい。この目的にはsetを使った方が良いよねというのは、本に書いてあるとおりだと思う。特に重複が多い場合はね。

>>> marxes.pop()
'Zeppo'
>>> marxes
['Groucho', 'Chiro', 'Harpo']
>>> marxes.count('Harpo')
1
>>> marxes.count('Bob')
0
>>> sn1_skit = ['cheeseburger', 'cheeseburger', 'cheeseburger']
>>> sn1_skit.count('cheeseburger')
3

最初のpop()は、本に書いてある状況と一緒にするため。count()で数えることができる。そのままだよね。

美しくないjoin再登場

>>> marxes
['Groucho', 'Chiro', 'Harpo']
>>> ', '.join(marxes)
'Groucho, Chiro, Harpo'

本では、ここでjoin()が美しく見えない形で生きながらえている(w)かの説明がある。でも、こういうもんだと呑み込んだ方が速いような気がする。split()がリストを返すのに対して、join()は文字列を返すので対の関係にあるというのは同意。

>>> friends = ['Harry', 'Hermione', 'Ron']
>>> spearator = ' * '
>>> joined = spearator.join(friends)
>>> joined
'Harry * Hermione * Ron'
>>>
>>> separated = joined.split(spearator)
>>> separated
['Harry', 'Hermione', 'Ron']
>>> separated == friends
True 

タイポしたまま突き進んでますけど何か?join()の動きが分かれば良いのです。でも、今回はspearator(separatorのつもり)を変数として渡して、spearator.join(friends)となっているので、あんまり違和感がない。そっか、', '.join()みたいに文字列をそのまま渡しているから気持ち悪かったんだな。

ソート

ソートのお話。

>>> marxes
['Groucho', 'Chiro', 'Harpo']
>>> sorted_marxes = sorted(marxes)
>>> sorted_marxes
['Chiro', 'Groucho', 'Harpo']
>>> marxes.sort()
>>> marxes
['Chiro', 'Groucho', 'Harpo']
>>> 

sorted()は汎用関数で、ソートされたリストのコピーを返す。sort()は、リストそのものをソートしちゃう。だから、書式も関数名が前に来るか、.関数名()になるかも異なっていると理解。

>>> numbers = [2, 1, 4.0, 3]
>>> numbers.sort()
>>> numbers
[1, 2, 3, 4.0]
>>> numbers = [2, 1, 4.0, 3]
>>> numbers.sort(reverse=True)
>>> numbers
[4.0, 3, 2, 1]
>>> mix = [4, 56, 2.3, -1.4, 'Cow']
>>> mix.sort()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unorderable types: str() < int()

リストの中は、厳密に同じ型じゃないとソートしてくれないわけではないという意味ですね。intとfloatなら考えてSortしてくれているけど、さすがにstrが混じるとダメ。どうしてもソートしたい場合は、各要素をstrに統一しちゃえばいいよね。

>>> strmix = []
>>> for elem in mix:
...     strmix.append(str(elem))
... 
>>> strmix
['-1.4', '2.3', '4', '56', 'Cow']
>>> strmix.sort()
>>> strmix
['-1.4', '2.3', '4', '56', 'Cow']

ふむ。これを見るとさっき怒られたときに途中までソートしちゃってるんだね。エラーになるならロールバックしてくれた方が嬉しいかもしれない。

>>> len(marxes)
3

リストに対してlen()をやってやると、要素数が返ってくる。

代入とコピーの話

この話は、足を掬われる可能性があるので真面目に確認しておいた方がよさそう。

>>> a = [ 1, 2, 3]
>>> a
[1, 2, 3]
>>> b = a
>>> b
[1, 2, 3]
>>> a[0] = 'surprise'
>>> a
['surprise', 2, 3]
>>> b
['surprise', 2, 3]
>>> b[0] = 'I hate surprise.'
>>> b
['I hate surprise.', 2, 3]
>>> a
['I hate surprise.', 2, 3]
>>> 

リストを作って、それを=演算子で別の変数にコピー。コピー元の要素を一部書き換えたところ、コピー先の要素も書き換わっている。その逆も然り。つまり、a=bやった場合は参照だけ渡しているという意味だね。

値をちゃんとコピーする場合は、以下の方法を使う必要あり。

>>> a = [1,2,3]
>>> b = a.copy()
>>> c = list(a)
>>> d = a[:]
>>> 
>>> a[0] = 'integer lists are boring'
>>> b
[1, 2, 3]
>>> c
[1, 2, 3]
>>> d
[1, 2, 3]
>>> c[1] = 'two'
>>> a
['integer lists are boring', 2, 3]
>>> b
[1, 2, 3]
>>> d
[1, 2, 3]
>>> d[0] = 'Uno'
>>> a
['integer lists are boring', 2, 3]
>>> b
[1, 2, 3]
>>> c
[1, 'two', 3]
>>> 

cは入れ子[[]]になるのかと思ったら、大丈夫なんだね。実際のところは、copy()を使えば良さそう。

(つづく)