【发布时间】:2014-07-19 12:24:02
【问题描述】:
我正在尝试制作一堆几何对象,这些对象具有其固有的几何属性(中心点、半径、长度等),以及有助于绘制它们的属性(例如 a 的 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()
【问题讨论】:
-
你不只是重新实现一个字典,而不是保护自己免受哈希冲突吗?为什么不使用您的元组作为字典中的键?
-
我使用集合的部分原因是哈希是一个不可变的副本,并且因为我最初将它存储为字典键(一次性值为
True)。然后我会做if self._get_hash() in self._hash_dict来检查我是否需要重新计算。然后我意识到我可以只检查平等而不是检查 dict 成员资格......所以我基本上把它改成了if self._get_hash() != self._hash:,因为它更轻量级。我还关心什么哈希冲突?我的形状的整个状态都存储在元组中,因此如果相等,则根据定义,它是相同的形状。 -
啊,忽略我的哈希冲突评论——我可以发誓我在那里看到了对
hash函数的调用,但显然我只是文盲。不过,我不明白您不愿意使用dict:使用 cache_dict 是标准模式(如果合适,也可以使用lru_cache。) -
我猜是因为 dicts 用于将无序的键序列映射到值,我不需要所有这些...我也刚刚看了Alex Gaynor's 'Fast Python, Slow Python' talk,它警告不要滥用复杂的数据结构。
-
已编辑以添加带有字典的实现......那更好/惯用/pythonic吗?
标签: python hash properties lazy-evaluation