🌊

python3にはflattenがないとか言われるけどyield fromがあるので別に困らない。

2023/09/23に公開

とある言語には配列のflatten()があるようで、いまだによくpythonが引き合いに出されるのだが、小生は正直困ったことがあまりない。
用途もさることながら、実装にもまったく苦にはならない。

PEP380 サブジェネレータへの移譲

python3.3以降には、PEP380 サブジェネレータへの移譲 が備わっているためだ。

https://peps.python.org/pep-0380/

https://docs.python.org/ja/3/whatsnew/3.3.html#pep-380-syntax-for-delegating-to-a-subgenerator

端的にいえば、自身のyeildに、他の iterable の出力を連結する機能である。

これを使えば、多段配列のflattenは、多段サブジェネレータとして実に簡潔に記述できる。

汎用性を考慮した上で馬鹿正直に書いてみた。

章題の通りで。 無限階層対応のflattenである。(事実上はスタックフレームの上限の影響を受ける)

https://github.com/kamawanu/zenn.dev-kamawanu-codes/blob/main/python3s/flatten.py

isiterable()は、builtinで有ると思ったらなかった。残念。

pythonでは、iterableを判定するのは __iter__() を持っているかどうかのみらしい。
tupleとlistもgeneratorもこれに当てはまるので、エコな判定方法であるとは言える。

加えて、文字列 str インスタンスも実は iterable であるという落とし穴があるにはある。
これを"馬鹿正直"に回避すると、isiterableのような有様になる。

やや不本意だが iterable であれば未知のオブジェクトでも確実に展開はするはずだ。

入力をlistに絞ればisiterableはもっとサボれる。strかlistかを判定できれば良いので別関数にする必然性も薄れる。
わざわざtuple返しを用意してるのは「配列実体を返したい」という需要もどうやらあるらしいことを考慮したが故。

基本的に小生はgeneratorのままで使うことが多い。
正味せいぜい5行。これなら毎回書いてもまるで苦になるまい。

ネットにはやたらワンライナーにこだわる一派もあるが、yield from のほうがメモリ効率的には有利だと思われる。

https://qiita.com/hoto17296/items/e1f80fef8536a0e5e7db

flatten は yield from でできるよ。 と書いてるひともいるんだが....

ちょっと調べたら、2015年からyield from で flattenできますよという投稿は見当たる。
貼ってあるソースも似たようなものだ。

https://qiita.com/trsqxyz/items/013ea4ece0de52328f17

moduleもあることはある。

たかだが5行のために使うのもなあ。

https://pypi.org/search/?q=flatten

Discussion