Python 将属性与try和except一起使用的正确方法

Python 将属性与try和except一起使用的正确方法,python,properties,Python,Properties,我有一些类的属性设置为@propertydecorator。它们使用内部的try和except子句充当getter和setter。若并没有设置该属性,它将从数据库中获取数据,并使用它从其他类中恢复对象。我尽量使示例简短,但用于实例化属性对象的代码对于每个属性都有点不同。他们的共同点是尝试,除了一开始 class SubClass(TopClass): @property def thing(self): try: return self.

我有一些类的属性设置为
@property
decorator。它们使用内部的try和except子句充当getter和setter。若并没有设置该属性,它将从数据库中获取数据,并使用它从其他类中恢复对象。我尽量使示例简短,但用于实例化属性对象的代码对于每个属性都有点不同。他们的共同点是尝试,除了一开始

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

我的问题是:这是一种正确的(如蟒蛇式)做事方式吗?在我看来,在所有内容之上添加“尝试除外”部分似乎有点尴尬。另一方面,它使代码保持简短。或者有更好的定义属性的方法吗?

只要您至少使用Python 3.2,就可以使用
functools.lru\u cache()
decorator

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,则会有一个功能齐全的lru_缓存后端口,尽管在本例中,您调用它时没有传递任何参数,您可能会用更简单的东西替换它

@YAmikep询问如何访问
lru\u cache
cache\u info()
方法。它有点凌乱,但您仍然可以通过property对象访问它:

>>> C.foo.fget.cache_info()
CacheInfo(hits=0, misses=1, maxsize=128, currsize=1)

太棒了。实际上,我仍然使用2.7,但是ActiveState实现看起来很不错,很高兴知道它已经存在于Python3.2中。正如你所说,我可能会写一个更轻的版本。谢谢你,我将把这标记为一个答案。@Duncan:当使用lazy_属性装饰器时,我们如何访问cache_info()?由于属性包装functools.lru\u缓存,因此无法再访问该缓存<代码>c.foo.cache_info()不起作用。。Thanks@YAmikep我更新了我的答案,以显示一种方法来做到这一点。好的,谢谢。有一件事我不太明白:缓存在类上?然后它被所有实例共享?是否不可能为每个实例设置缓存?我想在实例的生命周期内缓存实例上的属性,但是如果我创建了多个实例,并且共享缓存已满,那么第一个实例的缓存值将被删除,对吗?ThanksIs
lazy_属性
是否真的而不仅仅是缓存?