1行の関数で構成されたデータ変更パイプラインの単体テスト


10

メアリーローズクックの「関数型プログラミング入門」を読んで、アンチパターンの例として

def format_bands(bands):
    for band in bands:
        band['country'] = 'Canada'
        band['name'] = band['name'].replace('.', '')
        band['name'] = band['name'].title()

以来

  • 関数は複数のことを行います
  • 名前は説明的ではありません
  • 副作用があります

提案された解決策として、匿名関数のパイプライン化を提案します

pipeline_each(bands, [call(lambda x: 'Canada', 'country'),
                      call(lambda x: x.replace('.', ''), 'name'),
                      call(str.title, 'name')])

ただし、これにはテストしにくいという欠点があります。少なくともformat_bandsには、意図したとおりに機能するかどうかを確認する単体テストがありますが、パイプラインをテストする方法はありますか?あるいは、匿名関数は説明不要なので、テストする必要がないという考えですか?

このための実際のアプリケーションは、pandasコードをより機能的にすることです。「変更」機能の中にある種のパイプラインがあることがよくあります

def munge_data(df)
     df['name'] = df['name'].str.lower()
     df = df.drop_duplicates()
     return df

または、パイプラインスタイルで書き換えます。

def munge_data(df)
    munged = (df.assign(lambda x: x['name'].str.lower()
                .drop_duplicates())
    return munged

このような状況でのベストプラクティスに関する提案はありますか?


4
これらの個々のラムダ関数は、ユニットテストには小さすぎます。最終結果をテストします。言い換えると、匿名関数は単体テストできないため、個別に単体テストを計画している場合は、関数を匿名関数として記述しないでください。
Robert Harvey

回答:


1

あなたはおそらく本の修正された例のより重要な部分を見逃したと思います。コードに対するより根本的な変更は、リスト内のすべての値を操作するメソッドから、1つの要素を操作する方法への変更です。

リスト内のすべての要素に対して特定の操作を実行するiter(この場合はという名前のpipeline_foreach)関数がすでに存在します。これをforループで複製する必要はありませんでした。また、よく知られたリスト操作を使用すると、意図が明確になります。mapあなたと一緒に値を変換しています。ではiter、あなたは、各要素と副作用を実行しています。ではfor、あなたがそれに目を通すまでループあなたは...よく、あなたは本当に知りませんされています。

修正されたコードの例は、あまり機能的ではありません。これは、(私が知る限り)リスト内の値を返さずに変更し、それ以上のパイプや関数の構成を防ぐためです。機能的に好ましい方法mapは、更新されたcountryおよびでバンドの新しいリストを作成しますname。次に、その出力を次の関数にパイプするかmap、バンドリストを取得する別の関数で構成できます。でiter、それはパイプラインの行き止まりのようなものです。

最終結果コードには小さな関数が含まれているため、ここではテストを行うのは簡単ではありません。結局のところ、replaceまたはに対して単体テストを作成する必要はありませんtitle。これらを一緒に独自の関数と単体テストにまとめて、単一のアイテムで目的の組み合わせが達成されるようにしたいと思うかもしれません。私自身、おそらく単数形に変更format_bandsformat_band、forループを削除して、を呼び出しただけでしょうpipeline_each(bands, format_band)。次に、format_bandをテストして、何かを忘れていないことを確認します。

とにかく、あなたのコードに移ります。コードの2番目の例は、よりパイプライン的に見えます。しかし、それだけでは関数型プログラミングの利点は得られません。実際には、関数型プログラミングとは、入力と出力に関してのみ互換性を定義することにより、関数と他の関数の互換性を保証することを意味します。関数内に隠れた副作用がある場合、その入出力が他の関数と整列していても、実行時まで互換性があるかどうかはわかりません。ただし、2つの関数に副作用がなく、出力と入力が一致している場合は、予期しない結果を心配することなくパイプライン処理または合成できます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.