今さらPython3 (25) - 内包表記(Comprehensions)
引き続き第4章です。
- 作者: Bill Lubanovic,斎藤康毅,長尾高弘
- 出版社/メーカー: オライリージャパン
- 発売日: 2015/12/01
- メディア: 単行本(ソフトカバー)
- この商品を含むブログを見る
Comprehensionsを日本語表記すると内包表記となるんですね。
リストの内包表記
1~5までの整数が入ったリストを作りましょう。
>>> number_list = [] >>> number_list.append(1) >>> number_list.append(2) >>> number_list.append(3) >>> number_list.append(4) >>> number_list.append(5) >>> number_list [1, 2, 3, 4, 5] >>> >>> number_list = [] >>> for number in range(1,6): ... number_list.append(number) ... >>> number_list [1, 2, 3, 4, 5] >>> number_list = list(range(1,6)) >>> number_list [1, 2, 3, 4, 5] >>>
どの方法でもできるけど、more Pythonic wayは内包表記を使う事ですとのこと。
>>> number_list = [number for number in range(1,6)] >>> number_list [1, 2, 3, 4, 5] >>> number_list = [number-1 for number in range(1,6)] >>> number_list [0, 1, 2, 3, 4] >>>
あ、これまで何回か使ってきたやつね。やっとアタマの中でつながったよ。
>>> a_list = [number for number in range(1,6) if number % 2 == 1] >>> a_list [1, 3, 5]
内包表記の中で条件も入れられるから、下みたいなソースを書かなくても良いよねということ。
>>> a_list = [] ... if number %2 == 1: ... a_list.append(number) ... >>> a_list [1, 3, 5]
今度は入れ子のケースも内包表記でイケるという話。
>>> rows = range(1,4) >>> cols = range(1,3) >>> for row in rows: ... for col in cols: ... print(row,col) ... 1 1 1 2 2 1 2 2 3 1 3 2 >>> >>> rows = range(1,4) >>> cols = range(1,3) >>> cells = [(row, col) for row in rows for col in cols] >>> cells [(1, 1), (1, 2), (2, 1), (2, 2), (3, 1), (3, 2)] >>> for cell in cells: ... print(cell) ... (1, 1) (1, 2) (2, 1) (2, 2) (3, 1) (3, 2) >>> for row, col in cells: ... print(row, col) ... 1 1 1 2 2 1 2 2 3 1 3 2 >>>
念のため確認で、行列の組み合わせをタプル(tuple)にしない場合を試してみた。
>>> cells = [row, col for row in rows for col in cols] File "<stdin>", line 1 cells = [row, col for row in rows for col in cols] ^ SyntaxError: invalid syntax >>> cells = [(row, col) for row in rows for col in cols] >>> cells [(1, 1), (1, 2), (2, 1), (2, 2), (3, 1), (3, 2)] >>>
怒られるのね。
辞書、セットの内包表記
リストがイケるなら辞書もという話。
>>> word = 'letters' >>> letter_counts = {letter: word.count(letter) for letter in word} >>> letter_counts {'e': 2, 't': 2, 'l': 1, 'r': 1, 's': 1} >>> letter_counts = {letter: word.count(letter) for letter in set(word)} >>> letter_counts {'s': 1, 't': 2, 'e': 2, 'l': 1, 'r': 1} >>>
各文字をキーにして、文字の使用回数を値として辞書にしている訳ですね。最初の方が同じ文字を2回カウントしているのは分かるけど、setにした方が速いのかは要確認かも。setの生成で時間が掛かるかもしれないし。ま、文字の重複度との兼ね合いだな。
>>> a_set = {number for number in range(1,6) if number %3 == 1} >>> a_set {1, 4}
1~5の範囲で3で割ると1余る整数のセットができますと。
でもタプルの内包表記はない
>>> number_thing = (number for number in range(1,6)) >>> number_thing <generator object <genexpr> at 0x007D1C10> >>> type(number_thing) <class 'generator'> >>> number_thing[1] Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: 'generator' object is not subscriptable >>> for number in number_thing: ... print(number) ... 1 2 3 4 5 >>> number_list = list(number_thing) >>> number_list [] >>> list(number_thing) [] >>> for number in number_thing: ... print(number) ... >>>
ん?なんか本と動きが違うぞ。もう1回。
>>> number_thing = (number for number in range(1,6)) >>> for number in number_thing: ... print(number) ... 1 2 3 4 5 >>> number_list = list(number_thing) >>> number_list [] >>> number_thing <generator object <genexpr> at 0x007D1CB0> >>> for number in number_thing: ... print(number) ... >>>
変だと思ったら、ちゃんと説明が書いてありますね。generatorは1回しか使えないので、後から見ようとしても忘れられていると。なので、こんな感じでlistで渡して再利用するしかないみたい。
>>> number_thing = (number for number in range(1,6)) >>> number_list = list(number_thing) >>> number_list [1, 2, 3, 4, 5] >>> for number in number_list: ... print(number) ... 1 2 3 4 5 >>>
じゃ、タプルを作りたければ、こうすればいいんだよね?
>>> number_thing = (number for number in range(1,6)) >>> number_tuple = tuple(number_thing) >>> type(number_tuple) <class 'tuple'> >>> number_tuple (1, 2, 3, 4, 5) >>>
(つづく)