Python のライブラリ attrs
をご紹介します。
import attr
attrs
はカスタムクラスを作成するときの特殊メソッドの記述を省略できる機能を提供するライブラリです。具体的には、クラスのアトリビュート(プロパティ)とイニシャライザ、その他いくつかの特殊メソッドの定義を省略することができます。
名前がよく似た attr
というパッケージもあります。今回取り上げるのはそれではなく末尾に s がついた attrs の方なのでご注意ください。
こちらです。
こちらではありません。
attrs
も attr
もコード内では末尾に s
のつかない import attr
でインポートする点は共通なので注意が必要です。
インストール
インストールには pip を使いましょう。
pip install attrs
上述のとおり、ディストリビューションとしてのパッケージ名は attrs
(末尾に s
が付いたもの)なのでご注意ください。
使い方
サンプルコードを見ながら使い方を見ていきましょう。
import attr
@attr.s
class Order:
id = attr.ib()
created_at = attr.ib()
ここではクラス Order
が attr.s
でデコレートされています。また、クラス変数 id
と created_at
に attr.ib()
の戻り値が代入されています。
このコードで、 Order
クラスには id
と created_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 を使ってこのあたりが楽できるのはよいかもしれません。 他にもオリジナルのバリデータを指定したりなどさまざまなことができるので、興味のある方は公式のドキュメントをご覧になってみてください。 以上です。
参考
非公式:
- Using attrs for everything in Python | Hacker News
- Deciphering Glyph :: The One Python Library Everyone Needs
公式: