LoginSignup
0
0

More than 3 years have passed since last update.

長い内包表記をなんとかする方法

Posted at

長い内包はしんどい

Pythonでは一般に、コレクションは内包表記で生成する(と思う)。

しかし少し複雑になると、内包表記は読むのがしんどい。

# Fizz Buzzリストを無理やり内包で
fb = ["Fizz Buzz" if i % 15 == 0 
      else "Fizz" if i % 3 == 0 
      else "Buzz" if i % 5 == 0 
      else i 
      for i in range(30)]

これなら、for文を書いた方が見やすい。

だが初期化前のオブジェクトを露出させたまま、少しずつ変更を加える書き方は、(関数型嗜好が強いと)なんとなく罪悪感がある。

fb = []
for i in range(30):
    if i % 15 == 0:
        fb.append("Fizz Buzz")
    elif i % 3 == 0:
        fb.append("Fizz")
    elif i % 5 == 0:
        fb.append("Buzz")
    else:
        fb.append(i)

どうするか?

ジェネレータ+コレクタ

yieldでジェネレータを定義し、それを元にコレクションを作る書き方が可能。

def _fb():
    for i in range(30):
        if i % 15 == 0:
            yield "Fizz Buzz"
        elif i % 3 == 0:
            yield "Fizz"
        elif i % 5 == 0:
            yield "Buzz"
        else:
            yield i

fb = list(_fb)

もっと簡潔に

↑も悪くはないが、ジェネレータを定義した後、list関数を通し、変数に代入する手順が煩雑。

また、listにするのを忘れ、イテレータを直接変数で受けてしまうとバグの温床になりうる。

yieldを使った定義がそのままリストの定義として解釈されてほしい。

デコレータを使えば実現できる。

def comprehension(collector):
    def ret(func):
        return collector(func())
    return ret

# 以下を実行すると、変数「fb」にリストが代入される。
@comprehension(list)
def fb():
    for i in range(30):
        if i % 15 == 0:
            yield "Fizz Buzz"
        elif i % 3 == 0:
            yield "Fizz"
        elif i % 5 == 0:
            yield "Buzz"
        else:
            yield i

元ネタ

funcyというライブラリにあるcollectingというデコレータが第一の元ネタ。ジェネレータ関数を、リストを返す関数に変える。これを使うとFizz Buzzは以下のように書ける。

from funcy import collecting

@collecting
def _fb():
    for i in range(30):
        if i % 15 == 0:
            yield "Fizz Buzz"
        elif i % 3 == 0:
            yield "Fizz"
        elif i % 5 == 0:
            yield "Buzz"
        else:
            yield i

fb = _fb()

でも一旦関数を定義する手間が面倒。

Racketのfor/listみたいに書ければ……と思って、デコレータで関数を返さない解法を思いついた。

0
0
2

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
0
0