【问题标题】:Proper way of using properties with try and except通过 try 和 except 使用属性的正确方法
【发布时间】:2013-04-12 13:24:52
【问题描述】:

我的类具有使用@property 装饰器设置的属性。它们使用其中的 try 和 except 子句充当 getter 和 setter。如果未设置属性,它将从数据库中获取数据并使用它来实例化来自其他类的对象。我试图使示例简短,但是用于实例化属性对象的代码对于每个属性都略有不同。它们的共同点是一开始的 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 似乎有点尴尬。另一方面,它使代码保持简短。或者有没有更好的方法来定义属性?

【问题讨论】:

    标签: python properties


    【解决方案1】:

    只要您至少使用 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 询问如何访问lru_cachecache_info() 方法。有点乱,不过还是可以通过属性对象来访问的:

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

    【讨论】:

    • 这太棒了。我实际上仍在使用 2.7,但 ActiveState 实现看起来不错,很高兴知道它已经存在于 Python 3.2 中。正如你所说,我可能会写一个更轻松的版本。谢谢你,我会将此标记为答案。
    • @Duncan:我们如何在使用lazy_property 装饰器时访问cache_info()?由于属性包装了 functools.lru_cache,这不再是可访问的了吗? c.foo.cache_info() 不起作用.. 谢谢
    • @YAmikep 我更新了我的答案以显示一种方法。
    • 好的,谢谢。我不太明白的一件事:缓存在类上?然后由所有实例共享?不是每个实例都可以有一个缓存吗?我想在实例的生命周期内缓存实例上的属性,但是如果我创建了多个实例并且共享缓存已满,那么我的第一个实例的缓存值将被删除,对吗?谢谢
    • lazy_property 真的是lazy 而不仅仅是缓存吗?
    猜你喜欢
    • 2017-05-20
    • 1970-01-01
    • 2015-09-18
    • 2012-11-06
    • 2014-11-15
    • 1970-01-01
    • 2019-07-14
    • 1970-01-01
    • 2014-09-04
    相关资源
    最近更新 更多