python - 替换属性以获得性能增益

标签 python properties

情况

类似于this question , 我想更换一个属性。与那个问题不同,我不想在子类中覆盖它。我想在 init 和属性本身中替换它以提高效率,这样它就不必在每次调用属性时调用计算值的函数。

我有一个类,它有一个属性。构造函数可以获取属性的值。如果它传递了值,我想用值替换属性(不仅仅是设置属性)。这是因为属性本​​身会计算值,这是一项昂贵的操作。同样的,我想把属性计算出来的值替换成属性计算出来的值,这样以后调用属性就不用重新计算了:

class MyClass(object):
    def __init__(self, someVar=None):
        if someVar is not None: self.someVar = someVar

    @property
    def someVar(self):
        self.someVar = calc_some_var()
        return self.someVar

问题

上面的代码不起作用,因为 self.someVar = 没有替换 someVar 函数。它会尝试调用未定义的属性的 setter。

可能的解决方案

我知道我可以通过如下略微不同的方式实现相同的目的:

class MyClass(object):
    def __init__(self, someVar=None):
        self._someVar = someVar

    @property
    def someVar(self):
        if self._someVar is None:
            self._someVar = calc_some_var()
        return self._someVar

这会略微降低效率,因为每次调用该属性时都必须检查是否为 None。该应用程序对性能至关重要,因此这可能不够好,也可能不够好。

问题

有没有办法替换类实例的属性?如果我能够这样做(即避免 None 检查和函数调用),效率会提高多少?

最佳答案

您要找的是Denis Otkidach优秀的 CachedAttribute:

class CachedAttribute(object):    
    '''Computes attribute value and caches it in the instance.
    From the Python Cookbook (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):
        # record the unbound-method and the name
        self.method = method
        self.name = name or method.__name__
        self.__doc__ = method.__doc__
    def __get__(self, inst, cls):
        # self: <__main__.cache object at 0xb781340c>
        # inst: <__main__.Foo object at 0xb781348c>
        # cls: <class '__main__.Foo'>       
        if inst is None:
            # instance attribute accessed on class, return self
            # You get here if you write `Foo.bar`
            return self
        # compute, cache and return the instance's attribute value
        result = self.method(inst)
        # setattr redefines the instance's attribute so this doesn't get called again
        setattr(inst, self.name, result)
        return result

可以这样使用:

def demo_cache():
    class Foo(object):
        @CachedAttribute
        def bar(self):
            print 'Calculating self.bar'  
            return 42
    foo=Foo()
    print(foo.bar)
    # Calculating self.bar
    # 42

请注意,随后访问 foo.bar 不会调用 getter 函数。 (Calculating self.bar 未打印。)

    print(foo.bar)    
    # 42
    foo.bar=1
    print(foo.bar)
    # 1

foo.__dict__ 中删除 foo.bar 会重新公开 Foo 中定义的属性。 因此,再次调用 foo.bar 会再次重新计算该值。

    del foo.bar
    print(foo.bar)
    # Calculating self.bar
    # 42

demo_cache()

装饰器发表于 Python Cookbook也可以在 ActiveState 上找到.

这是高效的,因为虽然该属性存在于类的 __dict__ 中,但在计算之后,在实例的 __dict__ 中创建了一个同名的属性。 Python 的属性查找规则优先考虑实例的 __dict__ 中的属性,因此类中的属性实际上被覆盖了。

关于python - 替换属性以获得性能增益,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7388258/

相关文章:

css - getComputedStyle 返回 CSSStyleDeclaration 但访问时所有属性均为空

python - 如何在 matplotlib 2.0 中填充只有影线(无背景色)的区域

python - OpenCV VideoCapture 无法在 Python 中读取视频,但可以在 VS11 中读取

javascript - 列出对象的内置属性

c# - 我在属性 setter 中进入无限循环

c# - Winforms PropertyGrid - 属性不可编辑

Python str.contains 来自两个或多个字典

python - cx_freeze 和捆绑文件

python - pandas - 使用 for 循环将多列附加到数据框

properties - Semantic Mediawiki 1.8.0 不显示自定义命名空间中的页面属性