Python:表示自我包装的方形网格(圆柱体)
我正在模拟一个方形网格,这个网格是可以自我环绕的。也就是说,如果你向上走超过最高点,就会回到最低点,就像一个圆柱体一样;如果你向右走,就会碰到边界。我需要跟踪不同代理的位置、各个点的资源数量,并根据一些规则计算代理的移动方向。
那么,最好的建模方法是什么呢?
我是不是应该创建一个表示点的类,这个类里面有方法可以返回每个方向上相邻的点?如果这样做的话,我可能还需要让这个类可以被哈希,这样我才能把它作为字典的键,字典里存放整个网格(我想这样的网格应该用字典来表示吧?)
或者,我应该创建一个描述整个网格的类,而不把每个点当作独立的对象来处理?
又或者,我可以直接使用普通的(x, y)元组,然后在其他地方写方法来查找邻居?
我需要建模的很多内容现在还不太明确。此外,我还预计这个表面的几何形状将来可能会发生变化(例如,它可能会在两个方向上都环绕)。
补充问题:我应该把资源数量的信息附加到每个点的实例上,还是应该有一个单独的类来存放一个以点为索引的资源地图?
2 个回答
我需要跟踪不同代理的位置、各个地点的资源数量,并根据一些规则计算代理将要移动的方向。
听起来这就像是在说图(graph),不过我总是试着把每个问题都看成图。你提到的所有操作(移动、存储资源、找出要移动到哪里)在图中都是非常常见的。你还可以很容易地改变图的形状,比如从圆柱体变成环面,或者其他任何形状。
唯一的问题是,这种表示方式需要占用比其他方式更多的空间。
好的一面是,你可以使用一个图形库来创建这个图,甚至可能用一些图算法来计算代理的移动方向。
如果你想要一个可以哈希的 Point
类,而且不想花太多功夫,可以通过继承元组(tuple)来实现,并添加你自己的邻居方法。
class Point(tuple):
def r_neighbor(self):
return Point((self[0] + 1, self[1]))
def l_neighbor(self):
[...]
x = Point((10, 11))
print x
print x.r_neighbor()
元组的构造函数需要一个可迭代的对象,所以在使用 Point((10, 11))
时要用双括号;如果你想避免这样做,可以重写 __new__
方法(重写 __init__
是没有意义的,因为元组是不可变的):
def __new__(self, x, y):
return super(Point, self).__new__(self, (x, y))
这里也可以用到模运算——不过这要看你具体要做什么:
def __new__(self, x, y, gridsize=100):
return super(Point, self).__new__(self, (x % gridsize, y % gridsize))
或者可以支持任意维度的网格,然后在 __new__
中继续使用元组:
def __new__(self, tup, gridsize=100):
return super(Point, self).__new__(self, (x % gridsize for x in tup))
关于你提到的资源问题:由于 Point
是一个不可变的类,所以不太适合用来存储可能会改变的信息。使用 defaultdict 会比较方便;你不需要手动初始化它。
from collections import defaultdict
grid = defaultdict(list)
p = Point((10, 13))
grid[(10, 13)] = [2, 3, 4]
print grid[p] # prints [2, 3, 4]
print grid[p.r_neighbor] # no KeyError; prints []
如果你想要更多的灵活性,可以在 defaultdict 中使用字典而不是列表;但是 defaultdict(defaultdict)
是不行的;你需要创建一个新的 defaultdict 工厂函数。
def intdict():
return defaultdict(int)
grid = defaultdict(intdict)
或者更简洁地说
grid = defaultdict(lambda: defaultdict(int))
然后
p = Point((10, 13))
grid[(10, 13)]["coins"] = 50
print grid[p]["coins"] # prints 50
print grid[p.r_neighbor]["coins"] # prints 0; again, no KeyError