何かを書き留める何か

数学や読んだ本について書く何かです。最近は社会人として生き残りの術を学ぶ日々です。

Python 3.6における『Effective Python』 項目33はこう変わる

項目33「サブクラスをメタクラスで検証する」はこう書き換わる

Python 3.6がリリースされた。

Python 3.6で導入された新機能の一つに__init_subclass__がある。 PEP 487 -- Simpler customisation of class creation | Python.org か石本さんのエントリを参照してほしい。

atsuoishimoto.hatenablog.com

『Effective Python』の項目33「サブクラスをメタクラスで検証する」ではメタクラスを使ってサブクラスの検証(値の整合性チェックなど)を行うテクニックが解説されている。 多角形のクラスを定義する際に、頂点数が3未満の場合はValueErrorを送出する、というサンプルがある。

class ValidatePolygon(type):

    def __new__(meta, name, bases, class_dict):
        if bases != (object,):
            if class_dict["sides"] < 3:
                raise ValueError("polygons need 3+ sides.")
        return type.__new__(meta, name, bases, class_dict)


class Polygon(object, metaclass=ValidatePolygon):
    sides = None

    @classmethod
    def interior_angles(cls):
    """内角の総和を求める"""
        return (cls.sides - 2) * 180


class Triangle(Polygon):
    sides = 3


if __name__ == '__main__':
    triangle = Triangle()
    print(triangle.interior_angles())

Python 3.6では、__init_subclass__を利用することでメタクラスを使うことなくサブクラスの検証が実現できる。

class Polygon(object):

    def __init_subclass__(cls, sides, **kwargs):
        cls.sides = sides
        if cls.sides < 3:
            raise ValueError("polygons need 3+ sides.")

    @classmethod
    def interior_angles(cls):
        return (cls.sides - 2) * 180


class Triangle(Polygon, sides=3):
    pass


if __name__ == '__main__':
    triangle = Triangle()
    print(triangle.interior_angles())

__init_subclass__がクラスメソッドである、ということを押さえておけばメタクラスを使う例よりもわかりやすくなっている。

ベストプラクティスを収めた『Effective Python』も時間がたつと変わってしまう、利用している道具の変化は常にを追わないといけない、というのを強く感じる次第である。