Python環境でScala風リスト操作をできるようにするパッチを書いた

abst

こういう書き方ができるようになるパッチ書いた

[1, 2, 3, 4].map(_ * 10).reduce(_ + _) 

motivation

かねてよりpythonmap,filter,reduceはうっとしくてかなわん、と思っていた。何より、 method chain にまったく向いていない。

例えば、Scalaでは以下の様なコード

val sum_evens = Seq(1, 2, 3, 4, 5).filter(_ % 2 == 0).map(_ * 10).fold(0)(_ + _)

これが、Pythonではこうなる

sum_evens = reduce(lambda x, y: x + y, map(lambda y: y * 10, filter(lambda x: x % 2 == 0, [1, 2, 3, 4, 5])))

つらい。

pythonic とか Scala 使えとかいうのは忘れる。unpythonicで結構なのでこれを何とかしたい。

patch for built-in types

Python は monkey patch のやりやすい言語だが、built-in typesに対しては普通にはできない。方法はあるはずと探しに探したところ、強制的にパッチする gist をみつけた。

コメントには以下のようにある。

 # found this from Armin R. on Twitter, what a beautiful gem ;)

Python界隈では大変有名な Armin Ronacher 先生作らしい(結局オリジナルは見つけられなかった)。

これだ!というわけで、ざっくりまとめてgithubに上げた。

https://github.com/kogecoo/prototype.py

ネーミングは同僚より。

install

pipに上げたら怒られそうな内容なので上げてない。 cloneしてinstallする必要がある。

$ git clone https://github.com/kogecoo/prototype.py.git
$ cd prototype.py
$ python setup.py install

usage

先ほどのコードはこのように書けるようになる。

from prototype import itr
itr.attach()

[1, 2, 3, 4, 5].filter(lambda x%2 == 0).map(lambda x: x*10).reduce(lambda x, y: x+y)

さらに、Scala-like な lambdaを書けるようにする(fn.py)https://github.com/kachayev/fn.py を使えば、そこはもうScala World...

from prototype import itr
from fn import _
itr.attach()

[1, 2, 3, 4].map(_ * 10).reduce(_ + _)  #  100

調子に乗って、ScalaParSeq 的なこともできるようにした。

from prototype import par
from operator import add

par.attach()

def mult10(x):
    return x * 10

if __name__=='__main__':
    [[1, 2], [3, 4]].par().flatmap(mult10).reduce(add)

残念なことにmultiprocessingの制約があってlambdaは使えない。fn.pyも無理。 Pathosというライブラリがこれを解決しそうだけど、pipがダメなので気長に待とう。

最後に

やればやるほどScalaで書けよと自分で突っ込みを入れたくなった。