如何创建装饰器以延迟初始化属性

发布于 2024-09-09 02:46:29 字数 793 浏览 12 评论 0原文

我想创建一个像属性一样工作的装饰器,只是它仅调用装饰函数一次,并且在后续调用中始终返回第一次调用的结果。一个例子:

def SomeClass(object):
    @LazilyInitializedProperty
    def foo(self):
        print "Now initializing"
        return 5

>>> x = SomeClass()
>>> x.foo
Now initializing
5
>>> x.foo
5

我的想法是为此编写一个自定义装饰器。所以我开始了,这就是我的进展:

class LazilyInitializedProperty(object):
    def __init__(self, function):
        self._function = function

    def __set__(self, obj, value):
        raise AttributeError("This property is read-only")

    def __get__(self, obj, type):
        # problem: where to store the value once we have calculated it?

如您所见,我不知道在哪里存储缓存的值。最简单的解决方案似乎是只维护一本字典,但我想知道是否有更优雅的解决方案。

编辑抱歉,我忘了提及我希望该属性是只读的。

I want to create a decorator that works like a property, only it calls the decorated function only once, and on subsequent calls always return the result of the first call. An example:

def SomeClass(object):
    @LazilyInitializedProperty
    def foo(self):
        print "Now initializing"
        return 5

>>> x = SomeClass()
>>> x.foo
Now initializing
5
>>> x.foo
5

My idea was to write a custom decorator for this. So i started, and this is how far I came:

class LazilyInitializedProperty(object):
    def __init__(self, function):
        self._function = function

    def __set__(self, obj, value):
        raise AttributeError("This property is read-only")

    def __get__(self, obj, type):
        # problem: where to store the value once we have calculated it?

As you can see, I do not know where to store the cached value. The simplest solution seems to be to just maintain a dictionary, but I am wondering if there is a more elegant solution for this.

EDIT Sorry for that, I forgot to mention that I want the property to be read-only.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

感性不性感 2024-09-16 02:46:29

Denis Otkidach 的 CachedAttribute 是一个方法装饰器,它使得惰性属性(计算一次,可访问多次)。为了使其只读,我添加了一个 __set__ 方法。为了保留重新计算的能力(见下文),我添加了一个 __delete__ 方法:

class ReadOnlyCachedAttribute(object):    
    '''Computes attribute value and caches it in the instance.
    Source: Python Cookbook 
    Author: Denis Otkidach https://stackoverflow.com/users/168352/denis-otkidach
    This decorator allows you to create a property which can be computed once and
    accessed many times. Sort of like memoization
    '''
    def __init__(self, method, name=None):
        self.method = method
        self.name = name or method.__name__
        self.__doc__ = method.__doc__
    def __get__(self, inst, cls): 
        if inst is None:
            return self
        elif self.name in inst.__dict__:
            return inst.__dict__[self.name]
        else:
            result = self.method(inst)
            inst.__dict__[self.name]=result
            return result    
    def __set__(self, inst, value):
        raise AttributeError("This property is read-only")
    def __delete__(self,inst):
        del inst.__dict__[self.name]

例如:

if __name__=='__main__':
    class Foo(object):
        @ReadOnlyCachedAttribute
        # @read_only_lazyprop
        def bar(self):
            print 'Calculating self.bar'  
            return 42
    foo=Foo()
    print(foo.bar)
    # Calculating self.bar
    # 42
    print(foo.bar)    
    # 42
    try:
        foo.bar=1
    except AttributeError as err:
        print(err)
        # This property is read-only
    del(foo.bar)
    print(foo.bar)
    # Calculating self.bar
    # 42

CachedAttribute 的美妙之处之一(以及
ReadOnlyCachedAttribute) 是如果你del foo.bar,那么下次你
访问foo.bar,该值被重新计算。 (这个魔法是通过
事实上,del foo.barfoo.__dict__中删除了'bar',但属性
bar 保留在 Foo.__dict__ 中。)

如果您不需要或不希望重新计算此功能,
那么以下(基于Mike Boers的lazyprop)是一种更简单的方法创建一个只读的惰性属性。

def read_only_lazyprop(fn):
    attr_name = '_lazy_' + fn.__name__
    @property
    def _lazyprop(self):
        if not hasattr(self, attr_name):
            setattr(self, attr_name, fn(self))
        return getattr(self, attr_name)
    @_lazyprop.setter
    def _lazyprop(self,value):
        raise AttributeError("This property is read-only")
    return _lazyprop

Denis Otkidach's CachedAttribute is a method decorator which makes attributes lazy (computed once, accessible many). To make it also read-only, I added a __set__ method. To retain the ability to recalculate (see below) I added a __delete__ method:

class ReadOnlyCachedAttribute(object):    
    '''Computes attribute value and caches it in the instance.
    Source: Python Cookbook 
    Author: Denis Otkidach https://stackoverflow.com/users/168352/denis-otkidach
    This decorator allows you to create a property which can be computed once and
    accessed many times. Sort of like memoization
    '''
    def __init__(self, method, name=None):
        self.method = method
        self.name = name or method.__name__
        self.__doc__ = method.__doc__
    def __get__(self, inst, cls): 
        if inst is None:
            return self
        elif self.name in inst.__dict__:
            return inst.__dict__[self.name]
        else:
            result = self.method(inst)
            inst.__dict__[self.name]=result
            return result    
    def __set__(self, inst, value):
        raise AttributeError("This property is read-only")
    def __delete__(self,inst):
        del inst.__dict__[self.name]

For example:

if __name__=='__main__':
    class Foo(object):
        @ReadOnlyCachedAttribute
        # @read_only_lazyprop
        def bar(self):
            print 'Calculating self.bar'  
            return 42
    foo=Foo()
    print(foo.bar)
    # Calculating self.bar
    # 42
    print(foo.bar)    
    # 42
    try:
        foo.bar=1
    except AttributeError as err:
        print(err)
        # This property is read-only
    del(foo.bar)
    print(foo.bar)
    # Calculating self.bar
    # 42

One of the beautiful things about CachedAttribute (and
ReadOnlyCachedAttribute) is that if you del foo.bar, then the next time you
access foo.bar, the value is re-calculated. (This magic is made possible by
the fact that del foo.bar removes 'bar' from foo.__dict__ but the property
bar remains in Foo.__dict__.)

If you don't need or don't want this ability to recalculate,
then the following (based on Mike Boers' lazyprop) is a simpler way to make a read-only lazy property.

def read_only_lazyprop(fn):
    attr_name = '_lazy_' + fn.__name__
    @property
    def _lazyprop(self):
        if not hasattr(self, attr_name):
            setattr(self, attr_name, fn(self))
        return getattr(self, attr_name)
    @_lazyprop.setter
    def _lazyprop(self,value):
        raise AttributeError("This property is read-only")
    return _lazyprop
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文