CPU密集型的从属属性

时间:2016-06-06 10:28:27

标签: python class properties

我经常遇到这种情况,并没有设法找到一种稳定的方法来处理它。

假设我有一个类定义的类:

class MyClass(object):
    def __init__(self, a, b):
        self.a = a
        self.b = b

    @property
    def c(self):
       """This method performs some heavy computations based on a and b properties"""
        # Some heavy computations only with a and b attributes
        return c

现在可以通过以下方式检索属性c

>>> obj = MyClass(a, b)
>>> print obj.c

但是,每次我要求obj.c时,都会执行繁重的计算,导致性能代码不佳,因为c会导致繁重的计算,并且最好只在{{1设置或修改{}或a

处理此案件的更好方法是什么?我正在考虑创建一个b方法,以用作某些c_update@a.setter装饰方法的装饰器,但这是更好的方法吗?

此致

4 个答案:

答案 0 :(得分:1)

  

但是如果我有很多依赖于a和b值的XX依赖属性呢?我是否必须为每个方法编写update_XX方法,并将此方法添加到init以及每个a.setter和b.setter?这在我看来相当冗长......

每当ca发生变异时,您都可以更新b值(以及任何其他数量的相关属性),我在下面实施了update_properties()方法:

class MyClass(object):
    def __init__(self, a, b):
        self._a = a
        self._b = b

        self.update_properties()

    @property
    def a(self):
        return self.a

    @a.setter
    def a(self, value):
        self._a = value
        self.update_properties()

    @property
    def b(self):
        return self._b

    @b.setter
    def b(self, value):
        self._b = value
        self.update_properties()

    def update_properties(self):
        self.c = self._a + self._b
        self.d = self._a * self._b
        self.e = self._a - self._b
        # self.f = ...
        # ...
        # self.z = ...
        # Can go on as long as you want
  

你认为有可能将这个机制作为一些装饰器来实现,以减轻代码

详细程度似乎仅在跟踪自由变量的一侧(例如此处ab),所以如果我必须支持任意数量的那些,我会实现MyClass.set_value(name, value)

def set_value(self, name, value):
    setattr(self, name, value)
    self.update_properties()

所以这里的想法是我们的​​set_value()可以使用任意数量的属性。如果您使用__init__解压缩传递给构造函数的键值,则可以从**kwargs调用它。

此处有一项要求,因为我们未将自由变量设置为@property我们需要使用obj.set_value('a', 42)代替obj.a = 42

答案 1 :(得分:0)

我只是将c的实际值存储在私有属性中,并检查这不是None。当Nonea发生变化时,请将此设置为b

因此,使用属性执行此操作的“正确”方法是:

class MyClass(object):
    def __init__(self, a, b):
        self._a = a
        self._b = b
        self._c = None

    @property
    def a(self):
        return self._a

    @a.setter
    def a(self, value):
        self._a = value
        self._c = None

    @property
    def b(self):
        return self._b

    @b.setter
    def a(self, value):
        self._b = value
        self._c = None

    @property
    def c(self):
        if self._c is None:
            self._c = # compute c here
        return self._c

如果您想避免创建所有这些属性和设置器,您可能想要劫持__getattr____setattr__方法:

class MyClass(object):
    def __init__(self, a, b):
        self.a = a
        self.b = b
        self._c = None

    def __getattr__(self, name):
        if name == 'c':
            if self._c is None:
                self._c = # compute c here
            return self._c
        raise AttributeError(name)

    def __setattr__(self, name, value):
        if name == 'c':
            raise TypeError('You cannot modify property c directly')
        super(MyClass, self).__setattr__(name, value)
        if name in ('a', 'b'):
            super(MyClass, self).__setattr__('_c', None)

请注意,最后一个解决方案可以扩展为10个属性a1,...,a10,而无需定义10个属性和设置器。

它可能不太健壮。

答案 2 :(得分:0)

因此,根据您的答案,我设法使用dict为依赖属性构建一个新答案。

class MyClass(object):
    def __init__(self, a, b):
        self._properties = dict()
        self._a = a
        self._b = b

    def _update_dependent_properties(self):
        # Do computations for c1, c2...
        self._properties['c1'] = # c1 value
        self._properties['c2'] = # c2 value
        # ...

    @property
    def a(self):
        return self._a
    @property
    def b(self):
        return self._b

    @a.setter
    def a(self, value):
        self._properties.clean()
        self._a = value

    @b.setter
    def b(self, value):
        self._properties.clean()
        self._b = value

    @property
    def c1(self):
        try:
            return self._properties['c1']
        except KeyError:
            _update_dependent_properties()
            return self._properties['c1']

    @property
    def c2(self):
        try:
            return self._properties['c2']
        except KeyError:
            _update_dependent_properties()
            return self._properties['c2']

这似乎可以解决这个问题,但它仍然非常冗长......我仍然要为我期望的每个依赖属性编写一个属性。但是,当update_dependent_properties()a属性被修改时,它会强制计算b

我想知道它是否不存在这样做的模块。似乎我的问题听起来像memoize技术......并且装饰者可以通过系统化程序来减轻代码吗?

答案 3 :(得分:0)

有一个小的pypi包非常适合:cached-property

from cached_property import cached_property

class MyClass(object):
    def __init__(self):
        pass

    @cached_property
    def c(self):
        # Heavy computation based on self.a / self.b
        return ...

    @property
    def a(self):
        return self._a

    @a.setter
    def a(self, value):
        self._a = value
        del self.c

    @property
    def b(self):
        return self._b

    @b.setter
    def b(self, value):
        self._b = value
        del self.c

当然,您还可以为a / b属性构建抽象,利用del self.c

使用cached_property的一个好处是,您可以通过将缓存更改为threaded_cached_property轻松地使缓存线程安全。