リストの内包表現
十分にPythonのコードを読んでいるなら、本質的にリストの内包表現として知られる簡潔で十分な構造に出くわすでしょう。
もし前に使っていないんなら一目惚れすることを期待するPythonの機能の一つです。このようなものなのです:
In [1]:
[i for i in range(20) if i % 3 > 0]
Out[1]:
[1, 2, 4, 5, 7, 8, 10, 11, 13, 14, 16, 17, 19]
この結果は3の倍数をのぞいた数のリストです。これはちょっと最初は困惑させるかもしれない例である一方で、Pythonが発展することに親しんでいると、リストの内包表現を読んで書いくことが次第に自然となるでしょう。
基本のリスト内包表現
リストの内包表現は、リストで作られるforループを読みやすい一つの短い行に圧縮する、簡単な一つの方法です。例えば、最初の12の平方数のリストを作るループがあります:
In [2]:
L = []
for n in range(12):
L.append(n ** 2)
L
Out[2]:
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121]
同じくこれのリストの内包表現は次のものです:
In [3]:
[n ** 2 for n in range(12)]
Out[3]:
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121]
多くのPythonの命令文で、平素な英語で、この文の意味をほとんど読み終えることができます:
"construct a list consisting of the square of n for each n up to 12".:
この基本なシンタックスは、それゆえ、[expr for var in iterable] となります、exprは任意の有効な表現がある場所で、varは変数名です、そしてiterableは任意の反復可能なPythonオブジェクトです。
二重の反復
ときどき、ただ一つの値からだけでなく、二つからリストを作りたいです。これをするため、単純に、内包表現での表現に他のを追加しましょう:
In [4]:
[(i, j) for i in range(2) for j in range(3)]
Out[4]:
[(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2)]
表現の二つ目が、結果のリストの中で、最も早くかわる、内部のインデックスとして動作することに留意しましょう。この種の構成は、内包表現の中で三つ、四つ、またはそれ以上のイテレータに拡張されます、もっともある地点でコードは苦しませるものとなるでしょう。
イテレータにおける条件
さらに、表現の最後に条件を追加して反復を管理できます。この節の最初の例で、1から20のすべての数を反復し切りましたが、3の倍数を除外しました。これを再びみてみましょう、そして構造に気づいてください。
In [5]:
[val for val in range(20) if val % 3 > 0]
Out[5]:
[1, 2, 4, 5, 7, 8, 10, 11, 13, 14, 16, 17, 19]
(i % 3 > 0) という表現は、3で割れないならTrueと等価です。再び英語の意味がすぐに読みあげられます: "Construct a list of values for each value up to 20, but only if the value is not divisible by 3".一度、それになれたら、これは、一目で理解するために書くのが、同じループシンタックスよりも、はるかに早くなります。 :
In [6]:
L = []
for val in range(20):
if val % 3:
L.append(val)
L
Out[6]:
[1, 2, 4, 5, 7, 8, 10, 11, 13, 14, 16, 17, 19]
値の条件
Cでプログラムしているなら、?演算子によって可能な一行条件条件に親しみがあるかもしれません:
int absval = (val < 0) ? -val : val
Pythonはこれにとても似たものをもってます、それはしばしばリストの内包表現やlambda関数、そして単純な表現が求められるその他他の場所の中で使われます:
In [7]:
val = -10
val if val >= 0 else -val
Out[7]:
10
これは単に組み込みのabs()関数の機能を重複するものですが、その構造はいくつか本当にリストの内包表記の中で興味深いものに感じさせます。これは、いまはかなり複雑ととらえるかもしれないですが、このようにかけます:
In [8]:
[val if val % 2 else -val
for val in range(20) if val % 3]
Out[8]:
[1, -2, -4, 5, 7, -8, -10, 11, 13, -14, -16, 17, 19]
リストの内包表現中で、表現にために前で行が離れていることに留意しましょう:Pythondではこれが有効で、しばしば、より高い可読性のために、長いリストの内包表現を折りたたむ良い方法です。これをみあげてみましょう:リストを作り、3の倍数を除外して、すべての2の倍数を否定していることです。
一度リストの内包表現の力学を理解すれば、それを他の型の内包表現へ動かすのはそのままです。シンタックスはとても同じようなものです。唯一の違いはあなたが使う括弧の種類です。
例えば、setの内包表現で、括弧でsetを作りましょう。
In [11]:
{n**2 for n in range(12)}
Out[11]:
{0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121}
setが重複無しのコレクションであることを呼び起こしましょう。setの内包表現はこの規則を守り、重複要素を消します。
In [12]:
{a % 3 for a in range(1000)}
Out[12]:
{0, 1, 2}
わずかな微調整で、辞書の内包表現をコロン(:)を加えて作ることができます。
In [13]:
{n:n**2 for n in range(6)}
Out[13]:
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25}
最後に、大括弧よりもむしろ括弧を使うなら、ジェネレータの表現を呼ぶこといになります。
In [15]:
(n**2 for n in range(12))
Out[15]:
at 0x1027a5a50>
ジェネレータの表現は本質的に、要素がすべて一気よりむしろ必要に応じて生成されるという点で、リストの内包表現です、そして単純さがここではこの言語の力強い機能になっています、これの詳細は次で探求します。
# Refs.
* [イテレータとジェネレータ](https://qiita.com/tomotaka_ito/items/35f3eb108f587022fa09)
* [平方数](https://ja.wikipedia.org/wiki/%E5%B9%B3%E6%96%B9%E6%95%B0)
square number.
* [List Comprehension](https://qiita.com/supersaiakujin/items/0776d3252c5000ca146e)