LoginSignup
3
5

More than 3 years have passed since last update.

Pythonのリスト内包表記でいつも混乱するので覚え書き

Last updated at Posted at 2020-02-22

はじめに

仕事で Python を使っていて、
「あー、ここのリスト作る処理、リスト内包表記にしたら簡潔に書けるのになー。」
と思うことがあります。
でも、私がリスト内包表記を書きたいと思う場面が意外と少ないため、いざ書こうと思ったときに、
「あれ、リスト内包表記って、どうやって書くんだっけ?うーん、時間もないし、いつも通りループにするか。」
と、残念な感じになるので、私自身のための覚え書きの意味も含めて。

リスト内包表記とは?

初めてこの言葉を聞いたときは、頭の中が?マークだらけに。。。
すごーく簡単に言うと、リスト内包表記とは、
リストデータを1行で作る方法
です。

ただし、何でも1行で書けるわけではなく、(若干語弊がありますが、)ループでリストを作成する処理を1行で書けるものです。
私自身、「仕事で書くコードは複雑なんだよ!」と思っていたのですが、意外と使えます。

書き方

通常のループ(これはリスト内包表記ではありません。)

data = []
for i in range(10):
    data.append(i)

print(data)
# [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

リスト内包表記

print([i for i in range(10)])
# [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

解説

通常のループと、リスト内包表記の結果は同じです。
しかし、リスト内包表記の方が、記述量が少なくてすみます。
そして、コードが簡潔で読みやすくなります。(慣れないと読みにくいですが。)

リスト内包表記のコードを見ていきます。

[...]
ご存知の通り、これでリストができるため、通常のループの data = [] が不要になります。

for i in range(10)
これは通常のループでも、リスト内包表記でも同じです。
ただし、リスト内包表記の方には、 : は不要です。

i for i in range(10)
最初にでてくる i は、ループで取り出した i (2つめの i )の値が格納されます。
ループのたびに最初にでてくる i の値が書き換わります。
その i が、ループの回数分、リストに追記(append)されます。

もう少し具体的に

もう少し具体的な方が理解しやすいと思うので、次のケースで説明します。

ケース1

リスト内のデータを加工したい場合。
例えば、 ['aa', 'b', 'ccc'] というリストの各データに、 @ を付加したい場合。

items = ['aa', 'b', 'ccc']
print([item + '@' for item in items])
# ['aa@', 'b@', 'ccc@']

items のデータに @ をつけて、別のリストを作成しています。
リストデータから、別のリストデータを1行で作ったというわけです。

ケース2

データ加工処理など、決まったルールで文字列の途中に、別の文字を付加したい場合。
例えば、abcdefgh というデータに1文字ごとに、 - を付加したい場合。

items = 'abcdefgh'
print('-'.join([item for item in items]))
# a-b-c-d-e-f-g-h

こちらは、文字列からリストデータを1行で作っています。
そのリストのデータを -join して文字列に戻しています。

[追記]
コメントをいただいたので、追記します。
リスト内包表記を使わなくても実現できました。無駄がなくていいです。

items = 'abcdefgh'
print('-'.join(items))
# a-b-c-d-e-f-g-h

ちなみに、2文字ごとに、 - を付加したい場合。

items = 'abcdefgh'
print('-'.join([items[i*2: i*2+2] for i in range(int(len(items)/2))]))
# ab-cd-ef-gh

ちょっと詰め込みすぎな感じがするので、こんな感じでしょうか。

items = 'abcdefgh'
loop_count = int(len(items)/2)
print('-'.join([items[i*2: i*2+2] for i in range(loop_count)]))
# ab-cd-ef-gh

[追記]
こちらもコメントをいただいたので、追記します。
上の場合は、i というカウンタを使うため、範囲外アクセスが起こる可能性があります。
それを回避した書き方です。

items = 'abcdefgh'
print('-'.join(ch1+ch2 for ch1, ch2 in zip(items[::2], items[1::2])))
# ab-cd-ef-gh

カウンタを使う場合、使わない場合でも、文字が奇数個の場合、最後の1文字が出力されないことがわかりました。
カウンタを使うことになりますが、回避できました。

import math
items = 'abcdefghi'
loop_count = math.ceil(len(items) / 2)
print('-'.join([items[i*2: i*2+2] for i in range(loop_count)]))
# ab-cd-ef-gh-i

最後に

ケース1では、リストのデータを加工して、別のリストを作る、
ケース2では、文字列を加工して、リストを作る、というサンプルです。

どちらのケースも、ループを使って実現できますが、
リスト内包表記を使えば、シンプルかつ簡潔にコードが書けます。

3
5
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
5