簡體   English   中英

如果在Python字典中查找失敗,則查找最近的密鑰對

[英]Finding the closest pair of keys if lookup fails in a 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個鍵。 沒有基本上迭代整個字典,這樣的事情可以嗎? 如果沒有,那么這個問題確實不需要回答。 如果這確實可行,請盡可能包括一些性能影響。 謝謝。

這是一個使用函數來訪問普通字典的解決方案(我使用OrderedDict因為我現在有一個舊版本的Python,如果你有Python 3.6或更多,你可以使用普通dict ,因為它們是有序的。)

我們按鍵對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)

你也可以__missing__ dict ,更新__missing__方法(Python> = 3.6的代碼,假設dict是有序的:

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)

自定義dict類的解決方案:

import bisect
import collections


class Bound:
    def __init__(self, left, right):
        self.left = left
        self.right = right

    def __repr__(self):
        return 'Bound({}, {})'.format(self.left, self.right)


class MyDict(collections.defaultdict):
    def __init__(self, *args, **kwargs):
        super().__init__()
        dict.__init__(self, *args, **kwargs)
        self.lst = sorted(key for key in self)

    def __setitem__(self, key, value):
        if key not in self:
            bisect.insort_left(self.lst, key)
        super().__setitem__(key, value)

    def __delitem__(self, key):
        self.lst.remove(key)
        super().__delitem__(key)

    def __missing__(self, key):
        right_index = bisect.bisect(self.lst, key)
        left_index = right_index - 1
        right = self.lst[right_index] if right_index != len(self.lst) else None
        left = self.lst[left_index] if left_index != -1 else None
        return Bound(left, right)


d = MyDict([(1, 0), (7, 10), (28, 20)])
print(d[-3]) # Bound(None, 1)
print(d[6]) # Bound(1, 7)
print(d[7]) # 10
print(d[33]) # Bound(28, None)
del d[7]
print(d[6]) # Bound(1, 28)
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)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM