通过散列依赖属性延迟更新计算属性的最佳方法

时间:2014-05-29 20:52:12

标签: python hash properties lazy-evaluation

我正在尝试制作一堆具有内在几何属性(中心点,半径,长度等)的几何对象,以及帮助绘制它们的属性(如x,y,z坐标)三角网,弧分辨率等。)。

由于计算x,y,z坐标对于某些形状来说是一项昂贵的任务(比如带有边缘倒圆的三角形棱柱),我不希望每次更改属性时都这样做,但只有当请求坐标。即便如此,如果形状的定义没有改变,也没有必要重新计算它们。

所以我的解决方案是创建一个“哈希”,它只是定义形状“状态”的所有参数的元组。如果散列不变,则可以重新使用先前计算的坐标,否则必须重新计算坐标。所以我使用哈希作为存储形状定义的签名或指纹的方法。

我认为我的工作原理,但我想知道是否有更强大的方法可以利用__hash__或id或其他东西来处理这个问题。这对我来说太过分了,但我愿意接受建议。

这是我对球体的实现。我正在使用Mayavi进行最后的绘图,如果你没有Mayavi,你可以跳过/忽略。

#StdLib Imports
import os

#Numpy Imports
import numpy as np
from numpy import sin, cos, pi


class Sphere(object):
    """
    Class for a sphere
    """
    def __init__(self, c=None, r=None, n=None):
        super(Sphere, self).__init__()

        #Initial defaults
        self._coordinates = None
        self._c = np.array([0.0, 0.0, 0.0])
        self._r = 1.0
        self._n = 20
        self._hash = []

        #Assign Inputs
        if c is not None:
            self._c = c

        if r is not None:
            self._r = r

        if n is not None:
            self._n = n

    @property
    def c(self):
        return self._c

    @c.setter
    def c(self, val):
        self._c = val

    @property
    def r(self):
        return self._r

    @r.setter
    def r(self, val):
        self._r = val

    @property
    def n(self):
        return self._n

    @n.setter
    def n(self, val):
        self._n = val

    @property
    def coordinates(self):
        self._lazy_update()
        return self._coordinates

    def _lazy_update(self):
        new_hash = self._get_hash()
        old_hash = self._hash
        if new_hash != old_hash:
            self._update_coordinates()

    def _get_hash(self):
        return tuple(map(tuple, [self._c, [self._r, self._n]]))

    def _update_coordinates(self):

        c, r, n = self._c, self._r, self._n

        dphi, dtheta = pi / n, pi / n
        [phi, theta] = np.mgrid[0:pi + dphi*1.0:dphi,
                                0:2*pi + dtheta*1.0:dtheta]

        x = c[0] + r * cos(phi) * sin(theta)
        y = c[1] + r * sin(phi) * sin(theta)
        z = c[2] + r * cos(theta)

        self._coordinates = x, y, z
        self._hash = self._get_hash()


if __name__ == '__main__':

    from mayavi import mlab

    ns = [4, 6, 8, 10, 20, 50]

    sphere = Sphere()

    for i, n in enumerate(ns):
        sphere.c = [i*2.2, 0.0, 0.0]
        sphere.n = n

        mlab.mesh(*sphere.coordinates, representation='wireframe')

    mlab.show()

正如所建议的,这是一个使用字典将哈希存储为密钥的版本:

#StdLib Imports
import os

#Numpy Imports
import numpy as np
from numpy import sin, cos, pi


class Sphere(object):
    """
    Class for a sphere
    """
    def __init__(self, c=None, r=None, n=None):
        super(Sphere, self).__init__()

        #Initial defaults
        self._coordinates = {}
        self._c = np.array([0.0, 0.0, 0.0])
        self._r = 1.0
        self._n = 20

        #Assign Inputs
        if c is not None:
            self._c = c

        if r is not None:
            self._r = r

        if n is not None:
            self._n = n

    @property
    def c(self):
        return self._c

    @c.setter
    def c(self, val):
        self._c = val

    @property
    def r(self):
        return self._r

    @r.setter
    def r(self, val):
        self._r = val

    @property
    def n(self):
        return self._n

    @n.setter
    def n(self, val):
        self._n = val

    @property
    def _hash(self):
        return tuple(map(tuple, [self._c, [self._r, self._n]]))

    @property
    def coordinates(self):
        if self._hash not in self._coordinates:
            self._update_coordinates()

        return self._coordinates[self._hash]

    def _update_coordinates(self):

        c, r, n = self._c, self._r, self._n

        dphi, dtheta = pi / n, pi / n
        [phi, theta] = np.mgrid[0:pi + dphi*1.0:dphi,
                                0:2 * pi + dtheta*1.0:dtheta]

        x = c[0] + r * cos(phi) * sin(theta)
        y = c[1] + r * sin(phi) * sin(theta)
        z = c[2] + r * cos(theta)

        self._coordinates[self._hash] = x, y, z


if __name__ == '__main__':

    from mayavi import mlab

    ns = [4, 6, 8, 10, 20, 50]

    sphere = Sphere()

    for i, n in enumerate(ns):
        sphere.c = [i*2.2, 0.0, 0.0]
        sphere.n = n

        mlab.mesh(*sphere.coordinates, representation='wireframe')

    mlab.show()

1 个答案:

答案 0 :(得分:0)

为什么不简单地留一个旗帜:

class Sphere(object):

    def __init__(self, ...): 
        ...
        self._update_coordinates()

    ...

    @c.setter
    def c(self, val):
        self._changed = True
        self._c = val

    ...

    def _lazy_update(self):
        if self._changed:
            self._update_coordinates()

    def _update_coordinates(self):
        ...
        self._changed = False