python3.8 meets jqfpy

最近手元の環境のpythonのバージョンが3.8になった(雑に yay -Syu をやっていた結果1)。

そう言えば、python3.8ではセイウチ演算子で知られるassignment expressionsが入ったので、jqに疲れたpythonistaのためという触れ込みで昔作っていたパッケージがもう少し便利になるかもしれない。

github.com

これ。

jqfpy?

このブログでも幾つか紹介していたっけ?

..していた模様。

ユースケースを網羅しているわけではないけれど。おそらく最初の記事を読めば雰囲気は分かるはず。

使いかた

get()で取り出したあとは内包表記のワンライナーというのが基本的な使いかた。--slurp--compact--squashなどで少しだけ入出力を整形できる。あとしれっとyamlにも対応しているのでyqなどの代わりにもなるかもしれない。

$ seq 1 10 | jqfpy --slurp -c
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
$ seq 1 10 | jqfpy --slurp -c '[int(n) * int(n) for n in get()]'
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

python3.8 meets jqfpy

jqfpy上での操作は基本ワンライナーなので、ワンライナーの表現力をあげるセイウチ演算子と相性が良い。

$ seq 1 10 | jqfpy --slurp -c '[(n := int(x)) * n for x in get()]'

ちょっとこの例はあんまり良くないかもしれない。一番わかり易い例は正規表現などを使った簡易grepのようなもの。なのだけれど。。

正規表現でマッチした結果を使って変換

これからあげる例はかなりトリビアルなのでごめんなさい。。

テキトウに文字列が欲しい。import thisでzen of python辺りを入力にするか。 そのうちのIfで始まるものだけを取り出す。

$ python -m this | grep -i -P "^if"
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.

このうちifにマッチしたもののうち,部分より手前の文字列を強調表示してみたいということにする。ちょっと無茶な例だけれど。してみたいとする。

強調後の表現は ** <matched text> ** みたいな形で表示されて入れば良いことにしよう。今までこのようなマッチ結果を利用した変換が混ざったコードはjqfpy上で書くのは辛かった。

python -m this | jqfpy --slurp 'import re; rx = re.compile(r"^if.*,", re.IGNORECASE); [line.replace(m.group(0), f"** {m.group(0)} **") for line in get() if (m := rx.search(line)) is not None]' -i raw --squash -r
** If the implementation is hard to explain, ** it's a bad idea.
** If the implementation is easy to explain, ** it may be a good idea.

:tada: できるようになりました。おしまい。というのが今回の記事。おしまい。

さいごに

jqfpyの中では、元々 ";" が改行として扱われるので、python上の便利モジュールをimportして丸投げくらいはできたのだけれど、さすがに内包表記中での評価結果を利用して変換みたいな事は今までやりづらかった。

python3.8が普段遣いになったので、もう少しjqfpyの表現の幅も広がったかもしれない。


  1. Arch Linuxというやつです https://www.archlinux.org/