[レポート]PyCon JP 2020 『関数型Pythonアンチパターン』[初心者向け]

2020.08.31

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

どーもsutoです。

2020年8月28日〜29日の2日間で、Pythonコミュニティイベント「PyCon JP 2020 Online」が開催されました。(#pyconjp)

Pythonに関わる様々なテーマで(JST)10:00〜18:00でセッションや催しのコンテンツが配信されました。配信されたセッションの動画、ドキュメントについてはYoutube、Speaker Deckにアップされています。(公式ページのスケジュールにリンクがあります)

今回はその中から『関数型Pythonアンチパターン』について紹介したいと思います。自分自身もコードを書く際に気をつけたい項目として覚えておきたかったので!

動画は以下をご覧ください。

セッション概要

公式の概要説明は以下となります。

Elevator Pitch

今日関数型プログラミングは一般的技法になりつつあります。うまく取り入れれば、状態変化によるバグを追放し、簡潔なコードを書けるでしょう。 ただし、Pythonの特性を考慮せぬまま他の言語の関数型スタイルを真似ると、趣味的で読みづらいコードになりかねません。 このトークでは、Pythonで実用的なコードを書く上で、たとえ関数型であっても避けるべき書き方を紹介します。具体的には、以下のアンチパターン/パターンを扱います。

  • 長い式、複雑なlambdaを書く/式を名付ける
  • map・filterを多用する/内包を使う
  • 内包やreduceを濫用する/ループを書き、隠蔽する
  • クロージャを濫用する/適切にクラスを定義する

Prerequisite knowledge required from the audience

Pythonの基礎的な文法の理解。 関数型プログラミングについての基礎知識や、関数型言語を扱った経験があると理解しやすいですが、必須ではありません。

Knowledge that the audience can take home

  • 関数型プログラミングとは何かという抽象的な理解
  • 関数型プログラミングで可読性を壊さないようにするTips
  • Pythonicで実用的なコードにおいて、内包表記や高階関数を活用するTips

セッションレポート

関数型Pythonについての説明

  • 関数型プログラミングの特徴
    • 状態変化を排除
    • 処理が進む中で変数の中身などが次々変化していくようなことはなるべく避けたい。処理の流れがわかりにくくなるため。
    • 状態によってコードの結果が変化することがないのでメンテやテストがしやすくなる。可読性が増す。
  • 高階関数の多用
    • 関数を引数や戻り値として扱う関数
    • 処理の入れ替えに自由がきく、抽象化されたコードが書ける
  • Pythonicとは
    • Pythonの言語機能にマッチしている、Pythonコミュニティの価値観にマッチしている(つまりPythonらしさを活かしたコード)

スライドの例にあるように、ifとforを多用したループ処理を高階関数(map、filter)に置き換えたからと言って、読みやすいコードとはならないものがある。

タイトルの「アンチパターン」とは、「関数型であるが、unPythonic」なものを3パターン紹介する。

アンチパターン1.長い式を連ねる

  • Pythonは長い式に向かないし書きづらい
    • 式の途中で改行をはさむときは、バックスラッシュ、全体をカッコで囲うなど
    • syntax errorの原因にもなりやすい

→対処法

  • 式を区切り、名付ける
    • 一々イコールで変数に格納しているように見えて冗長だが、各関数でどのような処理をしたかがわかりやすくなる

アンチパターン2.map/filterを使う

  • 高階関数と言ったら他の言語でもよく使われる関数として紹介されるが
    • あまりPythonicぽくない

→対処法

  • 内包表記を使う
    • mapからlambda式を使うより直接的なfor,if文を内包表記で囲った方が内容も理解しやすい
    • 内包表記の方が処理が若干速い(らしい、自分もそこまで使い込んだことはないが)
    • 開発者もmap/filterはPython3で廃止したがってたとのこと

  • ただし、partial関数を使う場合、組み合わせられるのはmap/filterだけ

アンチパターン3.内包/reduceを乱用する

  • パターン2で内包表記で行けと言っても、使いすぎては行けない
  • 式の中身が長文になっては意味がない(アンチパターン1に触れてきちゃう)

→対処法

  • ループを書いて、関数に状態変化を隠蔽
    • 何でも関数型でまとめすぎず、for,ifを使って処理内容の可読性を上げること
    • (つまりfor,ifの状態変化があるまとまった処理を関数として格納し、メイン処理側でその関数を呼び出すようなコードを書けってことかな。どの言語でもよくやる整理ですね)

まとめ

以上、『関数型Pythonアンチパターン』のセッションの紹介でした。発表者も本内容については守るべき絶対的なものではないと言っていますので、あくまでコードを見やすく理解しやすいように書くTipsとして参考になればと思います。

Pythonは多くのモジュールや関数があるので、forやifをある程度織り交ぜたコードで書いた方がぱっと見わかりやすいことがあるので共感できます。

使うモジュールによって引数が多くなるものもあり、どうしても関数が長文になってしまうことがありますが、そのときは改行とインデントでしっかり整理しながら書きたいですね。