以下是代码:
编辑****请不要再“无法使用无序字典回复”。我几乎已经知道了。我发布了这篇文章的可能性很大,或者有人有可行的想法。
#position equals some set of two dimensional coords
for name in self.regions["regions"]: # I want to start the iteration with 'last_region'
# I don't want to run these next two lines over every dictionary key each time since the likelihood is that the new
# position is still within the last region that was matched.
rect = (self.regions["regions"][name]["pos1"], self.regions["regions"][name]["pos2"])
if all(self.point_inside(rect, position)):
# record the name of this region in variable- 'last_region' so I can start with it on the next search...
# other code I want to run when I get a match
return
return # if code gets here, the points were not inside any of the named regions
希望代码中的注释能够很好地解释我的情况。让我说我最后在区域“delta”内(即,键名是delta,值将是定义它的边界的坐标集)并且我还有500个区域。我第一次发现自己处于区域delta时,代码可能没有发现这个,直到(假设),第389次迭代......所以它在发现之前进行了388次all(self.point_inside(rect, position))
计算。因为下次运行时我可能仍会处于增量状态(但我必须在每次代码运行时验证),如果密钥“delta”是第一个被for循环检查的那个,那将会很有帮助。
这个特定的代码可以为许多不同的用户每秒运行多次..因此速度至关重要。设计很常见,用户不会在一个区域内,并且所有500条记录可能需要循环通过并且将在没有匹配的情况下退出循环,但我希望通过加速整个程序来加快整个程序的速度。那些目前在其中一个地区的人。
我不希望以任何特定顺序对字典进行排序等额外开销。我只是希望它开始寻找与成功匹配的最后一个all(self.point_inside(rect, position))
也许这会有所帮助..以下是我正在使用的字典(只显示了第一条记录),所以你可以看到我编码到上面的结构......是的,尽管名字是“rect”in代码,它实际上检查立方体区域中的点。
{“regions”:{“shop”:{“flgs”:{“breakprot”:true,“placeprot”:true},“dim”:0,“placeplayers”:{“4f953255-6775-4dc6- a612-fb4230588eff“:”SurestTexas00“},”breakplayers“:{”4f953255-6775-4dc6-a612-fb4230588eff“:”SurestTexas00“},”protected“:true,”banplayers“:{},”pos1“:[ 5120025,60,5120208],“pos2”:[5120062,73,5120257],“ownerUuid”:“4f953255-6775-4dc6-a612-fb4230588eff”,“accessplayers”:{“4f953255-6775-4dc6-a612-fb4230588eff “:”SurestTexas00“}},更多,更多,更多...}
答案 0 :(得分:3)
您可以尝试在dict
的自定义子类中实现一些缓存机制。
您可以在self._cache = None
中设置__init__
,添加set_cache(self, key)
之类的方法来设置缓存,最后在调用默认值之前覆盖__iter__
到yield self._cache
__iter__
。
但是,如果您考虑this stackoverflow answer以及this one,这可能有点麻烦。
对于你在问题中写的内容,我会尝试在你的代码中实现这个缓存逻辑。
def _match_region(self, name, position):
rect = (self.regions["regions"][name]["pos1"], self.regions["regions"][name]["pos2"])
return all(self.point_inside(rect, position))
if self.last_region and self._match_region(self.last_region, position):
self.code_to_run_when_match(position)
return
for name in self.regions["regions"]:
if self._match_region(name, position):
self.last_region = name
self.code_to_run_when_match(position)
return
return # if code gets here, the points were not inside any of the named regions
答案 1 :(得分:2)
您可以直接使用迭代器,而不是for循环。这是一个使用迭代器执行与您想要的类似的示例函数:
def iterate(what, iterator):
iterator = iterator or what.iteritems()
try:
while True:
k,v = iterator.next()
print "Trying k = ", k
if v > 100:
return iterator
except StopIteration:
return None
不是在last_region
中存储区域名称,而是存储此函数的结果,类似于"指针"到了你离开的地方。然后,您可以使用这样的函数(如同在Python交互式解释器中运行一样显示,包括输出):
>>> x = {'a':12, 'b': 42, 'c':182, 'd': 9, 'e':12}
>>> last_region = None
>>> last_region = iterate(x, last_region)
Trying k = a
Trying k = c
>>> last_region = iterate(x, last_region)
Trying k = b
Trying k = e
Trying k = d
因此,您可以从中断的地方轻松恢复,但需要注意一个额外的警告:
>>> last_region = iterate(x, last_region)
Trying k = a
Trying k = c
>>> x['z'] = 45
>>> last_region = iterate(x, last_region)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 5, in iterate
RuntimeError: dictionary changed size during iteration
如您所见,如果您添加新密钥,则会引发错误。因此,如果您使用此方法,则每次向字典添加新区域时都需要确保last_region = None
。
答案 2 :(得分:1)
没错,字典是无序类型。因此,OrderedDict对你想做的事情没有多大帮助。
您可以将最后一个区域存储到您的班级中。然后,在下一次调用时,在检查整个字典之前检查最后一个区域是否仍然良好?
答案 3 :(得分:0)
TigerhawkT3是对的。在某种意义上,在给定的字典中没有保证的顺序或键,Dicts是无序的。如果迭代相同的字典,您甚至可以使用不同的键顺序。如果您需要订单,则需要使用OrderedDict或仅使用普通列表。您可以将您的dict转换为列表,并按照它所代表的顺序对其进行排序。
答案 4 :(得分:0)
在不知道您的对象是什么以及示例中的self
是用户实例还是环境实例的情况下,很难找到解决方案。但是,如果示例中的self
是 environment ,则其Class可以具有class属性,该属性是所有当前用户及其最后已知位置的字典(如果用户实例是可哈希的)。 / p>
像这样的东西
class Thing(object):
__user_regions = {}
def where_ami(self, user):
try:
region = self.__user_regions[user]
print 'AHA!! I know where you are!!'
except KeyError:
# find region
print 'Hmmmm. let me think about that'
region = 'foo'
self.__user_regions[user] = region
class User(object):
def __init__(self, position):
self.pos = position
thing = Thing()
thing2 = Thing()
u = User((1,2))
v = User((3,4))
现在,您可以尝试从class属性中检索用户的区域。如果有多个Thing
,他们将共享该类属性。
>>>
>>> thing._Thing__user_regions
{}
>>> thing2._Thing__user_regions
{}
>>>
>>> thing.where_ami(u)
Hmmmm. let me think about that
>>>
>>> thing._Thing__user_regions
{<__main__.User object at 0x0433E2B0>: 'foo'}
>>> thing2._Thing__user_regions
{<__main__.User object at 0x0433E2B0>: 'foo'}
>>>
>>> thing2.where_ami(v)
Hmmmm. let me think about that
>>>
>>> thing._Thing__user_regions
{<__main__.User object at 0x0433EA90>: 'foo', <__main__.User object at 0x0433E2B0>: 'foo'}
>>> thing2._Thing__user_regions
{<__main__.User object at 0x0433EA90>: 'foo', <__main__.User object at 0x0433E2B0>: 'foo'}
>>>
>>> thing.where_ami(u)
AHA!! I know where you are!!
>>>
答案 5 :(得分:0)
您说您不希望以任何特定顺序排序字典的额外费用&#34;。 什么开销?据推测,OrderedDict
在内部使用了一些额外的数据结构来跟踪密钥的顺序。但除非你知道这会花费你太多内存,否则OrderedDict
就是你的解决方案。这意味着分析您的代码并确保OrderedDict
是您瓶颈的来源。
如果您想要最干净的代码,只需使用OrderedDict
即可。它有一个move_to_back
方法,它接受一个键并将其放在字典的前面或最后。例如:
from collections import OrderedDict
animals = OrderedDict([('cat', 1), ('dog', 2), ('turtle', 3), ('lizard', 4)])
def check_if_turtle(animals):
for animal in animals:
print('Checking %s...' % animal)
if animal == 'turtle':
animals.move_to_end('turtle', last=False)
return True
else:
return False
我们的check_if_turtle
函数查找OrderedDict
turtle
密钥。如果找不到,则返回False
。如果 找到它,则返回True
,但不会在将turtle
键移至OrderedDict
的开头后返回。
让我们试一试。在第一次运行:
>>> check_if_turtle(animals)
Checking cat...
Checking dog...
Checking turtle...
True
我们看到它检查了turtle
之前的所有密钥。现在,如果我们再次运行它:
>>> check_if_turtle(animals)
Checking turtle...
True
我们看到它首先检查了turtle
密钥。