以特定键值

时间:2015-06-29 00:10:45

标签: python performance python-2.7 for-loop dictionary

以下是代码:

编辑****请不要再“无法使用无序字典回复”。我几乎已经知道了。我发布了这篇文章的可能性很大,或者有人有可行的想法。

#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“}},更多,更多,更多...}

6 个答案:

答案 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密钥。