内包表記の表現力

  • リスト内包表記でできること

よく、pythonのリスト内包表記っていうと用例としては、

[x ** 2 for x in range(10) if x % 2 == 0]

のようなワンライナーが引っかかります。通常のfor文のように複数の処理を書くことって出来ないと思ってやってました。たとえば、

c = C()
for i in range(10):
    c.method()
    print(i)

 こんな感じのどこにでもある処理を内包表記で書けないかなっと。ただ、そうすることの意味ってあんまりないように思えるし、内包表記の中はインデントが整ってなくてもいいので、そもそもインデントが整ってるfor文の形式の方がpythonの禅にかなってるのかなとは思ってるんですが。思いついたのがこちら

>>> c = C()
>>> [[c.method(), print(i)] for i in range(3)]
0
1
2
[[Object, None], [Object, None], [Object, None]]

ああ、そうかたったこれだけで済んだんだ。けどこういう書き方見かけないなぁ。(レベルの低いこと言ってたらすいません)とりあえず、メソッドや組み込み関数が返す値がリストに入る。

>>> c = C()
>>> [[c.method(), print(i)][0] for i in range(10)]
0
1
2
[Object, Object, Object]

いらない値は抜き取ることも容易です。

 二つの場合でランダムウォークするシミュレーションを書いてみる。

import random

Lx = 100
Ly = 100
N = 5
T = 2

class Agent:
    
    def __init__(self):
        self.pos = dict(x=random.randint(1,Lx),
                        y=random.randint(1,Ly))
        self.min = dict(dx=Lx,dy=Ly)

    def __str__(self):
        #return str(vars(self))
        return "x: {} y: {}".format(self.pos["x"], self.pos["y"])

    def random_walk(a):
        rr = random.random()
        if rr < 1/4:
            a.pos["x"] += 1
        elif rr < 1/2:
            a.pos["x"] -= 1
        elif rr < 3/4:
            a.pos["y"] += 1
        elif rr < 1:
            a.pos["y"] -= 1
        return a

if __name__ == "__main__":
    c = Agent()
    cs = [Agent() for i in range(N)]
    #いつもの書き方
    for j in range(T):
        print("timer: {}".format(j))
        for i in range(N):
            print(cs[i])
            cs[i].random_walk()

    #インデントぐちゃぐちゃ    
    [[[print("timer: {}".format(j))],
            [[print(cs[i]),
              cs[i].random_walk()]
             for i in range(N)]]
     for j in range(T)]

インデントから逃げたら括弧の嵐になっちゃったね。