Я хочу написать контейнерный класс, который действует как словарь (на самом деле происходит от dict). Ключами для этой структуры будут даты.

Когда ключ (то есть дата) используется для извлечения значения из класса, если дата не существует, то следующая доступная дата, предшествующая ключу, используется для возврата значения.

Следующие данные должны помочь объяснить концепцию дальше:

Date (key)      Value
2001/01/01      123
2001/01/02       42
2001/01/03      100
2001/01/04      314
2001/01/07      312
2001/01/09      321

Если я попытаюсь извлечь значение, связанное с ключом (дата) '2001/01/05', я должен получить значение, хранящееся под ключом 2001/01/04, поскольку этот ключ встречается раньше, чем ключ '2001/01/05' быть, если бы он существовал в словаре.

Для этого мне нужно иметь возможность выполнять поиск (в идеале двоичный, а не наивный цикл по каждому ключу в словаре). Я искал ключевые слова bsearch в словарях Python - но не нашел ничего полезного.

В любом случае, я хочу написать такой класс, который инкапсулирует это поведение.

Вот что у меня пока (не очень):

#
class NearestNeighborDict(dict):
#
"""
#
a dictionary which returns value of nearest neighbor 
if specified key not found
#
"""

def __init__(self, items={}):
    dict.__init__(self, items)


def get_item(self, key):
    # returns the item stored with the key (if key exists)
    # else it returns the item stored with the key
13
morpheous 2 Июл 2010 в 06:11

5 ответов

Лучший ответ

Вы действительно не хотите создавать подкласс dict, потому что вы не можете использовать его функциональность повторно. Скорее, создайте подкласс абстрактного базового класса {{X1} } (или MutableMapping, если вы также хотите иметь возможность изменять экземпляр после создания), реализуйте необходимые специальные методы для этой цели, и вы получите другие dict подобные методы «бесплатно» из азбуки.

Методы, которые вам нужно кодировать: __getitem____setitem__ и __delitem__, если вам нужна изменчивость), __len__, __iter__ и __contains__.

bisect стандартной библиотеки предоставляет вам все, что вам нужно необходимо эффективно реализовать их в верхней части отсортированного списка. Например...:

import collections
import bisect

class MyDict(collections.Mapping):
  def __init__(self, contents):
    "contents must be a sequence of key/value pairs"
    self._list = sorted(contents)
  def __iter__(self):
    return (k for (k, _) in self._list)
  def __contains__(self, k):
    i = bisect.bisect_left(self._list, (k, None))
    return i < len(self._list) and self._list[i][0] == k
  def __len__(self):
    return len(self._list)
  def __getitem__(self, k):
    i = bisect.bisect_left(self._list, (k, None))
    if i >= len(self._list): raise KeyError(k)
    return self._list[i][1]

Возможно, вы захотите поиграть __getitem__ в зависимости от того, что вы хотите вернуть (или хотите ли вы поднять) для различных угловых случаев, таких как "k больше, чем все ключи в self" ,

14
Alex Martelli 2 Июл 2010 в 02:31

Я бы расширил dict и переопределил методы __getitem__ и __setitem__ для хранения отсортированного списка ключей.

from bisect import bisect

class NearestNeighborDict(dict):
    def __init__(self):
        dict.__init__(self)
        self._keylist = []

    def __getitem__(self, x):
        if x in self:
            return dict.__getitem__(self, x)

        index = bisect(self._keylist, x)
        if index == len(self._keylist):
            raise KeyError('No next date')

        return dict.__getitem__(self, self._keylist[index])

    def __setitem__(self, x, value):
        if x not in self:
            index = bisect(self._keylist, x)
            self._keylist.insert(index, value)

        dict.__setitem__(self, x, value)

Правда, вам лучше наследовать от MutableMapping, но принцип тот же, и приведенный выше код может быть легко адаптирован.

0
Chris B. 2 Июл 2010 в 02:37

Модуль sortedcontainers предоставляет SortedDict, который поддерживает ключи в отсортированном порядке и поддерживает разделение на эти ключи. Этот модуль - чистый Python и реализации fast-as-C со 100% тестированием и часами стресса ,

Например:

from sortedcontainers import SortedDict

sd = SortedDict((date, value) for date, value in data)

# Bisect for the index of the desired key.
index = sd.bisect('2001/01/05')

# Lookup the real key at that index.
key = sd.iloc[index]

# Retrieve the value associated with that key.
value = sd[key]

Поскольку SortedDict поддерживает быструю индексацию, также легко смотреть вперед или позади вашего ключа. SortedDict также является MutableMapping, поэтому он должен хорошо работать в вашей системе типов.

5
GrantJ 10 Апр 2014 в 19:05

Почему бы просто не поддерживать отсортированный список из dict.keys () и искать его? Если вы используете подклассы dict, вы можете даже создать возможность сделать двоичную вставку в этот список при добавлении значений.

0
andyortlieb 2 Июл 2010 в 02:40

Используйте метод floor_key на bintrees.RBTree: https://pypi.python.org/pypi/ bintrees / 2.0.1

0
fwc 10 Окт 2014 в 12:14