LoginSignup
5
3

More than 1 year has passed since last update.

Pythonでイテレータメソッドチェーン

Last updated at Posted at 2022-12-10

実用的ではないと思います。ただやってみたかっただけです。

JavascriptやRustではおなじみのメソッドチェーン。例えばRustだとイテレータメソッドをひたすらつなげることができます。

    let v: Vec<i32> = (0..10)
                        .map(|x| x + 2)
                        .filter(|x| x % 2 == 1)
                        .map(|x| x * 2)
                        .collect();
    println!("result is {:?}", v);
    // 出力は以下のようになる
    // result is [6, 10, 14, 18, 22]

これをPythonで普通に書くとこんな感じ。

    v = list(
        map(lambda x: x * 2,
            filter(lambda x: x % 2 == 1,
                map(lambda x: x + 2, range(10))
            )
        )
    )
    print(f'result is {v}')
    # 出力はRust版と同じ
    # result is [6, 10, 14, 18, 22]

これはこれで全然いいんですけど、読みやすさのために改行+インデントでフォーマットしていくと、やたらとインデントが深くなってしまうのがちょっと嫌。あと先に適用する関数を後ろに(より内側に)書くのがメソッドチェーンの場合と逆センスなのも少しだけモヤっとする。

ということで、こんな感じのクラスを定義。

from collections.abc import Iterable
from typing import Callable, List


class DotChain(Iterable):
    def __init__(self, it: Iterable):
        self._it = it

    def __iter__(self):
        return self._it.__iter__()

    def map(self, op: Callable):
        return DotChain(map(op, self._it))

    def filter(self, op: Callable):
        return DotChain(filter(op, self._it))

    def collect(self) -> List:
        return list(self)

すると、メソッドチェーンっぽく書けるようになる。

    v = DotChain(range(10)) \
          .map(lambda x: x + 2) \
          .filter(lambda x: x % 2 == 1) \
          .map(lambda x: x * 2) \
          .collect()
    print(f'result is {v}')
    # 出力はRust版、Python版と同じ
    # result is [6, 10, 14, 18, 22]

改行をエスケープしないといけないのだけれど、書き方的にはかなりJavascriptやRustに近づいた。

はい、それだけです。

5
3
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
5
3