使用try和except使用属性的正确方法

Gem*_*mmu 4 python properties

我有使用@property装饰器设置属性的类.它们使用try和except子句作为getter和setter.如果未设置attribute,它将从数据库获取数据并使用它来从其他类中实例化对象.我试图保持示例简短,但用于实例化属性对象的代码与每个属性略有不同.他们的共同点是尝试 - 开头时除外.

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
Run Code Online (Sandbox Code Playgroud)

我的问题:这是一种适当的(例如pythonic)做事的方式吗?对我来说,在所有内容之上添加try-except-segment似乎有点尴尬.另一方面,它使代码保持简短.或者有更好的方法来定义属性吗?

Dun*_*can 9

只要你至少使用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)
Run Code Online (Sandbox Code Playgroud)

一个快速运行的例子:

>>> 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
Run Code Online (Sandbox Code Playgroud)

如果你有很多这些你可以组合装饰:

>>> 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
Run Code Online (Sandbox Code Playgroud)

如果你仍然使用旧版本的Python,那么在ActiveState上有一个功能齐全的lru_cache后端,尽管在这种情况下,当你调用它时你没有传递任何参数,你可以用更简单的东西替换它.

@YAmikep询问如何访问该cache_info()方法lru_cache.它有点乱,但你仍然可以通过属性对象访问它:

>>> C.foo.fget.cache_info()
CacheInfo(hits=0, misses=1, maxsize=128, currsize=1)
Run Code Online (Sandbox Code Playgroud)