ライブラリ: attrs

Python のライブラリ attrs をご紹介します。

import attr

attrs はカスタムクラスを作成するときの特殊メソッドの記述を省略できる機能を提供するライブラリです。具体的には、クラスのアトリビュート(プロパティ)とイニシャライザ、その他いくつかの特殊メソッドの定義を省略することができます。

名前がよく似た attr というパッケージもあります。今回取り上げるのはそれではなく末尾に s がついた attrs の方なのでご注意ください。

こちらです。

こちらではありません。

attrsattr もコード内では末尾に s のつかない import attr でインポートする点は共通なので注意が必要です。

インストール

インストールには pip を使いましょう。

pip install attrs

上述のとおり、ディストリビューションとしてのパッケージ名は attrs (末尾に s が付いたもの)なのでご注意ください。

使い方

サンプルコードを見ながら使い方を見ていきましょう。

import attr

@attr.s
class Order:
    id = attr.ib()
    created_at = attr.ib()

ここではクラス Orderattr.s でデコレートされています。また、クラス変数 idcreated_atattr.ib() の戻り値が代入されています。

このコードで、 Order クラスには idcreated_at という 2 つのアトリビュートが追加されました。また、それらのアトリビュートを考慮した形で __init__()__repr__() の 2 つのメソッドが自動的に作成されました。

確認のため Order のインスタンスを作ってみましょう。

o1 = Order(5, 1497500000)
print(o1.id)  # => 5
print(o1.created_at)  # => 1497500000
print(o1)
# => Order(id=5, created_at=1497500000)

コンストラクタに渡された第 1 引数が id に、第 2 引数が created_at にそれぞれ代入されていることが確認できます。インスタンスを print() で表示したときの出力がきれいになっていることも確認できますね。

引数はキーワード指定で渡すことも可能です。

o2 = Order(id=5, created_at=1497500000)
print(o2)
# => Order(id=5, created_at=1497500000)

attr.ib() で定義されたアトリビュートをイニシャライザに渡さないとどうなるでしょうか。

o2 = Order()
# => # TypeError: __init__() missing 2 required positional arguments: 'id' and 'created_at'

TypeError が出ました。

attr.ib()default という引数を渡すことでそのアトリビュートのデフォルト値を設定することもできます。デフォルト値が設定されたアトリビュートはコンストラクタの必須の引数ではなくなります。

import attr

@attr.s
class Order:
    id = attr.ib()
    created_at = attr.ib(default=0)

o4 = Order(id=10)
print(o4)
# => Order(id=10, created_at=0)

デフォルト値は固定の値で指定する方法の他に、ファクトリ機能を使って動的に値を生成する方法もあります。その場合は attr.Factory を使用します。

from datetime import datetime
import attr

@attr.s
class Order:
    id = attr.ib()
    created_at = attr.ib(default=attr.Factory(datetime.now))

o5 = Order(id=15)
o6 = Order(id=20)
print("{}\n{}".format(o5, o6))
# => Order(id=15, created_at=datetime.datetime(2017, 6, 15, 7, 1, 29, 262216))
# => Order(id=20, created_at=datetime.datetime(2017, 6, 15, 7, 1, 29, 262274))

また、 attrs を使って作られたクラスのインスタンスは比較演算子で比較ができるようになります。これはデコレータ attr.s が比較系の特殊メソッドを自動で登録してくれるためです。

from datetime import datetime
import attr

@attr.s
class Order:
    id = attr.ib()
    created_at = attr.ib(default=attr.Factory(datetime.now))

o7 = Order(25)
o8 = Order(23)
print(o7 < o8)  # => False

now = datetime.now()
o9  = Order(100, now)
o10 = Order(100, now)
print(o9 == o10)  # => True
print(o9 is o10)  # => False

各アトリビュートにはバリデーションロジックをつけることもできます。

from datetime import datetime
import attr
from attr.validators import instance_of

@attr.s
class Order:
    id = attr.ib(validator=instance_of(int))
    created_at = attr.ib(default=attr.Factory(datetime.now))

o11 = Order('15')
# => TypeError

バリデーションはインスタンスの生成時に加えてその他のタイミングでも行うことができます。

o12 = Order(50)
o12.id = 'invalid'
attr.validate(o12)
# => TypeError

シンプルなコードを書くだけで各種特殊メソッドが自動的に定義されるので少し Explicit ではない感じもしますが、ほぼ定型のコードを毎度書くのは少しわずらわしかったりもするので、 attrs を使ってこのあたりが楽できるのはよいかもしれません。 他にもオリジナルのバリデータを指定したりなどさまざまなことができるので、興味のある方は公式のドキュメントをご覧になってみてください。 以上です。

参考

非公式:

公式: