2017/06/16

ライブラリ: attrs

Python のパッケージ attrs をご紹介します。

import attr

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

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

こちらです。

- attrs : Python Package Index

こちらではありません。

- attr : Python Package Index

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 と attr.ib() という 2 つのものが使われています。これで Order クラスに id と created_at という 2 つのアトリビュートが追加されました。また、イニシャライザの引数に id と created_at が渡せるようになりました。

試しに Order インスタンスを作ってみましょう。

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

第 1 引数が id に、第 2 引数が created_at にそれぞれ渡されていることが確認できます。 `__init__()` をまったく書いていないのにこの挙動。これは attrs が裏側でよきようにやってくれているからです。

インスタンスを print() に渡したときの表示もきれいになっていることに注目してください。こちらも attrs の機能で、裏側でよきようにやってくれているためです。

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

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)

デフォルト値はファクトリ機能を使って動的な値にすることもできます。

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 を使って作られたクラスのインスタンスは比較演算子で比較できるようになります。これは attrs の機能を使ってクラスを書くと attrs が比較系のマジックメソッドを自動で登録してくれるためだそうです。

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

公式

attrs: Classes Without Boilerplate — attrs documentation
python-attrs/attrs: Python Classes Without Boilerplate

0 件のコメント: