2016-02-09 3 views
5

Можно ли сделать что-то вроде:Использование диапазона в качестве индекса словаря в Python

r = {range(0, 100): 'foo', range(100, 200): 'bar'} 

print r[42] 

> 'foo' 

Так что я хотел бы использовать числовой диапазон как часть индекса словаря. Чтобы усложнить ситуацию, я также хотел бы использовать мультииндексы, такие как ('a', range(0,100)). Таким образом, концепция должна быть идеально расширяемой для этого. Какие-либо предложения?

Аналогичный вопрос был задан как here, но меня интересует комплексная реализация, а не различные подходы к этому вопросу.

+1

Установлены ли диапазоны? скажем, все: 'range (x * 100, (x + 1) * 100)' для некоторого целого 'x'? – pseudoDust

+0

@pseudoDust: Да, фиксированные диапазоны – n1000

+0

Я отправлю ответ с более простым и эффективным решением, учитывая, что релаксация проблемы – pseudoDust

ответ

2

В качестве ключа можно использовать неизменяемые типы данных. Итак, нет списков.

Но вы можете использовать тюль для определения верхней и нижней границ.

r = {(0,100): 'foo', (100,200): 'bar'} 

Я хотел бы получить значение для 42 таким образом:

res = "" 
for (k1,k2) in r: 
    if (k1 < 42 and k2 > 42): 
     res = r[(k1,k2)] 
print(res) 

Но я признаю, что вам не нужен словарь для этого.

+0

отредактировал мой ответ –

+0

спасибо - как бы я сделал 'r [42]' сейчас? :) – n1000

+0

добавил его к моему ответу –

4

Если вы на Python 3.x вы можете использовать range объект в качестве ключа, но для получения значения вы будете делать что-то вроде этого:

In [33]: r = {range(0, 100): 'foo', range(100, 200): 'bar'} 

In [34]: { r[key] for key in r if 42 in key} 
Out[34]: {'foo'} 

Причина вы не можете сделать это в python2. x - это потому, что функция range в версии 2.7 возвращает список, и списки не могут использоваться в качестве ключей словаря, поскольку они не предоставляют действительный метод __hash__.

+0

Ницца, спасибо. Настало время перейти на Python 3. К сожалению, этот проект находится на 2.7. – n1000

0

Вы могли бы использовать это:

r=dict(zip(range(100),["foo"]*100)) 
r2=dict(zip(range(100,200),["bar"]*100)) 
r.update(r2) 
+0

Теперь это правильный способ сделать это! Я рад, что кто-то подчеркнул. – Pouria

+2

Это неэффективно. – styvane

3

В качестве альтернативного подхода, если вы пытаетесь искать значения, связанные с определенными диапазонами вы можете использовать встроенный в Python bisect библиотеки следующим образом:

import bisect 

def boundaries(num, breakpoints=[100, 200], result=['foo', 'bar']): 
    i = bisect.bisect(breakpoints, num-1) 
    return result[i] 

print boundaries(42) 

Это выведет:

foo 
0

Вы можете сделать это в одной строке

r = dict(zip(range(200),["foo"]*100+["bar"]*100)) 
0

Вы можете использовать list comprehension, чтобы получить нужный словарь:

d = dict([(i,'foo') for i in range(0, 100)] + [(i,'bar') for i in range(100, 200)]) 
print d 
2

Если у вас есть простой способ вычислить диапазон от точки, например, если все диапазоны фиксированного размера можно использовать :

def getIndex(p): 
    start = floor(p/100)*100 
    return (start, start+100) 

Тогда имеет Dict определенное:

r = {(0, 100): 'foo', (100, 200): 'bar'} 

и доступ:

r[getIndex(42)] 

Этот метод эффективен, как:

  1. Вы не удерживанием в Словаре для каждого числа в диапазоне (это также позволяет иметь реальное значение «ключи «)
  2. вы не собираетесь, хотя весь Dict, чтобы найти значение, получая значение является O (1)

Кроме того, ваш getIndex может быть более сложным, например, если ваши диапазоны нерегулярны по длине, ваша функция getIndex может бинарно искать отсортированный список границ диапазона и возвращать кортеж диапазона, не более O(1), но O(log(n)) не является плохим ...

Смежные вопросы