如果在Python字典中查找失败,则查找最接近的键对

时间:2019-03-08 18:53:48

标签: python dictionary

让我们说我有一个Python字典,其中的键实际上是整数。我可以这样创建一个:

>>> d = dict([(1, 0), (7, 10), (28, 20)])
>>> d
{1: 0, 7: 10, 28: 20}

现在,我想进行查找,如果找到了密钥,则返回其索引。这部分真的很容易,就像这样:

>>> key = 7
>>> d[key]
10

如果未找到密钥,那么我想返回密钥的绑定。例如:

>>> key = 6
>>> d[key]
Bound(1, 7)

由于6不作为键存在,因此我返回了它们之间的2个键。 是否可以在基本上不迭代整个字典的情况下完成此事?如果没有,那么实际上不需要回答这个问题。如果确实可行,请尽可能包含一些性能影响。谢谢。

3 个答案:

答案 0 :(得分:5)

这是一个使用函数访问普通字典的解决方案(我使用了OrderedDict,因为我现在使用的是Python的较旧版本,如果您使用的是Python 3.6,则可以使用普通的dict或更多(按订单订购)。

我们按键对字典进行排序,这使我们可以使用bisect快速找到周围的键。

import bisect
from collections import OrderedDict

d = OrderedDict(sorted([(1, 0), (7, 10), (28, 20)])) # Could be a simple dict with Python 3.6+

class Bound:
    def __init__(self, a, b):
        self.a = a
        self.b = b

    def __repr__(self):
        return 'Bound({}, {})'.format(self.a, self.b)

def closest(key, d):
    try:
        return d[key]
    except KeyError:
        keys = list(d.keys())
        ins_point = bisect.bisect(keys, key)
        return Bound(keys[ins_point-1] if ins_point >= 1 else None,
                     keys[ins_point] if ins_point < len(keys) else None)

closest(7, d)
# 10

closest(8, d)
# Bound(7, 28)

closest(30, d)
# Bound(28, None)

closest(-1, d)
# Bound(None, 1)

您还可以继承dict的子类,更新__missing__方法(假定dict是有序的,则Python> = 3.6的代码:

import bisect

class Bound:
    def __init__(self, a, b):
        self.a = a
        self.b = b

    def __repr__(self):
        return 'Bound({}, {})'.format(self.a, self.b)


class BoundDict(dict):
    def __missing__(self, key):
        keys = list(self.keys())
        ins_point = bisect.bisect(keys, key)
        return Bound(keys[ins_point-1] if ins_point >= 1 else None,
                     keys[ins_point] if ins_point < len(keys) else None)


d = BoundDict(sorted([(1, 0), (7, 10), (28, 20)])) 

print(d[7])
# 10

print(d[8])
# Bound(7, 28)

print(d[30])
# Bound(28, None)

print(d[-1])
# Bound(None, 1)

答案 1 :(得分:2)

使用自定义字典类的解决方案:

runId = sys.argv[1]
trth = TrThDownload(runId)
data = trth.data
concurrences = min(len(data),10)
p = pool.ThreadPool(concurrences)
p.map(trth.runDownloader, data)
p.terminate()
p.close()
p.join()

答案 2 :(得分:0)

def bound(x, d):

  if x in d:
    return x
  else:

    for i in sorted(d):
      if x > i:
        l = i

    for j in sorted(d, reverse=True):
      if j > x:
        h = j

    return(l,h)


d = dict([(1, 0), (7, 10), (28, 20), (4,5), (2,5), (15,10)])

bound(17,d)

(15,28)