替换属性以获得性能增益 [英] Replace property for perfomance gain

查看:65
本文介绍了替换属性以获得性能增益的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

Similar to this question, I want to replace a property. Unlike that question, I do not want to override it in a sub-class. I want to replace it in the init and in the property itself for efficiency, so that it doesn't have to call a function which calculates the value each time the property is called.

我有一个类,上面有一个属性.构造函数可以获取属性的值.如果传递了值,我想用值替换属性(不仅仅是设置属性).这是因为属性本​​身会计算值,这是一项开销很大的操作.同样,我想在计算完属性后将其替换为该属性计算出的值,以便以后对该属性的调用不必重新计算:

I have a class which has a property on it. The constructor may take the value of the property. If it is passed the value, I want to replace the property with the value (not just set the property). This is because the property itself calculates the value, which is an expensive operation. Similarly, I want to replace the property with the value calculated by the property once it has been calculated, so that future calls to the property do not have to re-calculate:

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.

Problem

The above code does not work because doing self.someVar = does not replace the someVar function. It tries to call the property's setter, which is not defined.

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

I know I can achieve the same thing in a slightly different way as follows:

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 .应用程序对性能至关重要,因此这可能不够好,也可能不够.

This will be marginally less efficient as it will have to check for None every time the property is called. The application is performance critical, so this may or may not be good enough.

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

Is there a way to replace a property on an instance of a class? How much more efficient would it be if I was able to do this (i.e. avoiding a None check and a function call)?

推荐答案

您正在寻找的是 Denis Otkidach 优秀的 CachedAttribute:

What you are looking for is Denis Otkidach's excellent 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 不打印.)

Notice that accessing foo.bar subsequent times does not call the getter function. (Calculating self.bar is not printed.)

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

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

Deleting foo.bar from foo.__dict__ re-exposes the property defined in Foo. Thus, calling foo.bar again recalculates the value again.

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

demo_cache()

装饰器发布在 Python 手册 也可以在 ActiveState 上找到.

The decorator was published in the Python Cookbook and can also be found on ActiveState.

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

This is efficient because although the property exists in the class's __dict__, after computation, an attribute of the same name is created in the instance's __dict__. Python's attribute lookup rules gives precedence to the attribute in the instance's __dict__, so the property in class becomes effectively overridden.

这篇关于替换属性以获得性能增益的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆