flake8-class-attributes-orderで変数やメソッドの定義順を統一しよう!

flake8のプラグインflake8-class-attributes-orderの紹介です
2020.04.22

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

CX事業本部@大阪の岩田です。

私が現在関わっている案件は何度かメンバーの入れ替わりがあり、コーディング規約が微妙に統一できていない部分がありました。具体的には以下のようにパブリックなメソッドとプライベートなメソッドの定義がゴチャゴチャと混在している状況でした。

sample.py

class SomeClass:

    def __private_method1():
        print('pprivate method 1')

    def public_method1():
        print('public method 1')

    @staticmethod
    def static_method1():
        print('static method 1')

    def public_method2():
        print('public method 2')

    def __private_method2():
        print('pprivate method 2')

    @staticmethod
    def static_method2():
        print('static method 2')

後々の保守性を考えるとプライベートメソッドはプライベートメソッドでまとめて定義し、パブリックメソッドはパブリックメソッドでまとめて定義したいところです。対策のためにflake8のプラグインflake8-class-attributes-orderを導入してみたので利用方法等ご紹介します。

flake8-class-attributes-orderとは?

その名の通りクラスの属性(コンストラクタ、変数、メソッド等広い意味での属性)の定義順をチェックし、規約通りに定義されていない場合に警告してくれるflake8のプラグインす。開発元はロシア企業のBestDoctor社で、GitHubのリポジトリはこちらです。

https://github.com/best-doctor/flake8-class-attributes-order

やってみる

実際に試してみましょう。なお、今回使用した環境は以下の通りです。

  • Python: 3.7
  • flake8-class-attributes-order: v0.1.0

まずはpipを使ってflake8-class-attributes-orderをインストールします。

$ pip install flake8-class-attributes-order

これで依存関係のあるflake8もまとめてインストールされます。インストールできたら先程のクラス定義をチェックしてみます。

$ flake8 sample.py

チェックが完了すると以下のように警告が出力されます

sample.py:3:5: CCE001 SomeClass.__private_method1 should be after SomeClass.public_method1
sample.py:6:5: CCE001 SomeClass.public_method1 should be after SomeClass.static_method1
sample.py:16:5: CCE001 SomeClass.__private_method2 should be after SomeClass.static_method2

出力されたメッセージに従って、クラスの定義を以下のように書き換えてみます。

sample2.py

class SomeClass:

    @staticmethod
    def static_method1():
        print('static method 1')

    @staticmethod
    def static_method2():
        print('static method 2')

    def public_method1():
        print('public method 1')

    def public_method2():
        print('public method 2')

    def __private_method1():
        print('pprivate method 1')

    def __private_method2():
        print('pprivate method 2')

この状態で再度チェックにかけてみます。

$ flake8 sample2.py

今度は警告無しでチェックが完了します。

設定など

デフォルトの設定

flake8-class-attributes-orderをデフォルトのまま使用すると、クラスの属性が以下の順序になっていることをチェックします。

  • __new__
  • __init__
  • __post_init__
  • その他のマジックメソッド
  • プロパティ
  • スタティックメソッド
  • クラスメソッド
  • その他のメソッド
  • プライベートなメソッド

Strict mode

さらにStrict modeという組み込みのルールを利用することで

  • __new__
  • __init__
  • __post_init__
  • その他のマジックメソッド
  • プロパティ
  • スタティックメソッド
  • クラスメソッド
  • その他のメソッド
  • プライベートな プロパティ
  • プライベートなスタティックメソッド
  • プライベートなクラスメソッド
  • その他のプライベートなメソッド

の順序になっていることをチェックできます。Strict modeによるチェックは

$ flake8 --use-class-attributes-order-strict-mode sample.py

のように--use-class-attributes-order-strict-modeオプションを付与することで実行可能です。もしくは.flake8 に以下のように記述しておくことで、オプションの指定を省略することも可能です。

[flake8]
use_class_attributes_order_strict_mode = True

独自ルールの定義

デフォルトの設定やStrict modeとは異なるルールでチェックをかけたい場合は、独自ルールを定義することも可能です。独自ルールの定義は--class-attributes-orderオペションにクラスの属性をカンマ区切りで渡すことで定義可能です。

$ flake8 --class-attributes-order=private_method,method,static_method <sample2></sample2>.py

この例では

  • プライベートメソッド
  • その他メソッド
  • スタティックメソッド

の順番になるような独自ルールを指定しています。この独自ルールでチェックを実行すると結果は以下のようになります。

sample2.py:7:5: CCE001 SomeClass.static_method2 should be after SomeClass.public_method1
sample2.py:14:5: CCE001 SomeClass.public_method2 should be after SomeClass.__private_method1

先程はチェックOKだったsample2.pyがNGに変わったことが分かります。

独自ルールの定義で指定可能な属性はREADMEに記載されているので、詳細についてはREADMEを参照してみて下さい。メタクラスやネステッドクラス等の順序まで指定可能です。なお、独自ルールの定義についてもその他のオプションと同様に.flake8に設定を記述しておくことが可能です。.flake8を利用する場合は以下のように設定します。

[flake8]
class_attributes_order =
    field,
    meta_class,
    nested_class,
    magic_method,
    property_method,
    static_method,
    class_method,
    method,
    private_method

注意点

現在flake8-class-attributes-orderの最新版はv0.1.0ですが、__new____str__などの特殊メソッドに関するエラーメッセージが正しく生成されないという不具合があります。

例えば

class A:

    def foo(self):
      pass

    def __init__(self):
        pass

というクラス定義に対してチェックを実行すると

CCE001 A.foo should be after A.__init__

というエラーメッセージが出力されるのが期待値ですが、実際のエラーメッセージは

CCE001 A.foo should be after A.None

となります。

こちらのプルリクで修正は完了しているので、新バージョンがリリースされるのを待ちましょう。

まとめ

flake8-class-attributes-orderのご紹介でした。その他のflake8のプラグインと併用しつつ、Gitのpre-commit hookで自動チェックすることでソースコードの保守性が担保できそうです。以下の記事も参考にしつつ、是非試して見て下さい。