読者です 読者をやめる 読者になる 読者になる

何かを書き留める何か

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

『Effective Python』Item 30: 属性をリファクタリングする代わりに@propertyを検討しよう

Effective Python Python

『Effective Python』の続き。@property大好き一番好きなデコレータです! www.effectivepython.com

@propertyを使えばクラス全体をリファクタリングすることなく属性のリファクタリングができますよ、という話。 リーキーバケット - WikipediaPythonで実装するという題材で説明している。

from datetime import datetime, timedelta

class Bucket(object):
    def __init__(self, period):
        self.period_delta = timedelta(seconds=period)
        self.reset_time = datetime.now()
        self.quota = 0

    def __repr__(self):
        return 'Bucket(quota=%d)' % self.quota

が最初の実装である。

ここに、クォータの最大量と消費量を追加したいという要望があったとする。 勿論、self.quotaを書き換えればよいのだが、既に利用者がself.quotaを使ってしまっている…という状況でも、

class Bucket(object):
    def __init__(self, period):
        self.period_delta = timedelta(seconds=period)
        self.reset_time = datetime.now()
        self.max_quota = 0
        self.quota_consumed = 0

    def __repr__(self):
        return ('Bucket(max_quota=%d, quota_consumed=%d)' %
                (self.max_quota, self.quota_consumed))

@property
def quota(self):
    return self.max_quota - self.quota_consumed

@quota.setter
def quota(self, amount):
    delta = self.max_quota - amount
    if amount == 0:
        # Quota being reset for a new period
        self.quota_consumed = 0
        self.max_quota = 0
    elif delta < 0:
        # Quota being filled for the new period
        assert self.quota_consumed == 0
        self.max_quota = amount
    else:
        # Quota being consumed during the period
        assert self.max_quota >= self.quota_consumed
        self.quota_consumed += delta

とすれば、内部でself.quotaが「現在量」「最大量から消費量を差し引く」と実装が変わっていても、 クラスの利用者はself.quotaを「現在量」としてそのまま扱うことが出来る、という流れである。

なお、筆者は@propertyが好きなようである。

I especially like @property because...

とあるぐらい。 なお、次回も@propertyの例である。