10

@propertyデコレータで設定された属性を持つクラスがあります。これらは、内部の try 句と except 句を使用してゲッターとセッターとして機能します。属性が設定されていない場合、データベースからデータを取得し、それを使用して他のクラスからオブジェクトをインスタンス化します。例を短くしようとしましたが、属性オブジェクトをインスタンス化するために使用されるコードは属性ごとに少し異なります。それらに共通しているのは、最初の try-except です。

class SubClass(TopClass):

    @property
    def thing(self):
        try:
            return self._thing
        except AttributeError:
            # We don't have any thing yet
            pass
        thing = get_some_thing_from_db('thing')
        if not thing:
            raise AttributeError()
        self._thing = TheThing(thing)
        return self._thing

    @property
    def another_thing(self):
        try:
            return self._another_thing
        except AttributeError:
            # We don't have things like this yet
            pass
        another_thing = get_some_thing_from_db('another') 
        if not another_thing:
            raise AttributeError()
        self._another_thing = AnotherThing(another_thing)
        return self._another_thing

    ...etc...

    @property
    def one_more_thing(self):
        try:
            return self._one_more_thing
        except AttributeError:
            # We don't have this thing yet
            pass
        one_thing = get_some_thing_from_db('one') 
        if not one_thing:
            raise AttributeError()
        self._one_more_thing = OneThing(one_thing)
        return self._one_more_thing

私の質問: これは適切な (たとえば pythonic) 方法ですか? すべての上に try-except-segment を追加するのは少し厄介に思えます。一方で、コードを短く保ちます。または、属性を定義するより良い方法はありますか?

4

1 に答える 1

18

少なくとも Python 3.2 を使用している限り、functools.lru_cache()デコレータを使用してください。

import functools
class SubClass(TopClass):

    @property
    @functools.lru_cache()
    def thing(self):
        thing = get_some_thing_from_db('thing')
        if not thing:
            raise AttributeError()
        return TheThing(thing)

簡単に実行可能な例:

>>> import functools
>>> class C:
    @property
    @functools.lru_cache()
    def foo(self):
        print("Called foo")
        return 42


>>> c = C()
>>> c.foo
Called foo
42
>>> c.foo
42

これらがたくさんある場合は、デコレータを組み合わせることができます。

>>> def lazy_property(f):
    return property(functools.lru_cache()(f))

>>> class C:
    @lazy_property
    def foo(self):
        print("Called foo")
        return 42


>>> c = C()
>>> c.foo
Called foo
42
>>> c.foo
42

まだ古いバージョンの Python を使用している場合は、 ActiveStateに lru_cache の完全な機能を備えたバックポートがありますが、この場合、呼び出すときにパラメーターを渡していないため、おそらくもっと単純なものに置き換えることができます。

@YAmikep は のメソッドにアクセスするcache_info()方法を尋ねますlru_cache。少し面倒ですが、プロパティ オブジェクトからアクセスできます。

>>> C.foo.fget.cache_info()
CacheInfo(hits=0, misses=1, maxsize=128, currsize=1)
于 2013-04-19T07:53:00.297 に答える