有没有一种方法可以将字典的键设置为一个范围?

3

如果这很明显请原谅,但我对Python非常新手。我已经找到了从字典中获取多个键的方法,但这不是我想要做的。

基本上,我正在寻找像这样的东西:

my_dict = { "1-10" : "foo",
            "11-20" : "bar",
            # ...
            "91-100" : "baz" }

...但是键不一定是字符串,给定范围内的任何数字都可以映射到值。因此,例如,my_dict [9] 应该返回 foo,就像 my_dict [3] 一样。我考虑使用显式数组,如下所示,但它没有起作用:

my_dict = { [1, 2, 3, ..., 10] : "foo",

我不确定这是否是字典的有效用例,或者是否有其他数据结构可以使用。但Python总是让我感到惊讶。那么,有人知道魔法方法使其工作吗?


1
这个可能会对你有兴趣。 - inspectorG4dget
6个回答

5
我必须说我从未有过这样做的需求,也没有内置的数据结构可供使用。(如果你对哈希有所了解,你就会明白为什么字典不能以此方式工作。)
一种可能的解决方案是根本不使用字典,而是使用单独的键列表和值列表,其中键列表是每个"范围"的开头。所以:
keys = [0, 10, 20, 30]
values = ['foo', 'bar', 'baz', 'quux']

现在,您可以使用bisect来查找相关的键:

import bisect
pos = bisect.bisect_left(keys, 12)
value = values[pos-1]

1
+1 对于完全不同的方法。那么像(1-1012-20)这样的间隙呢? - Stefan

2

这绝对不是一个常见的情况,我建议使用显而易见的解决方案:

my_dict = dict((i, "foo") for i in range(1,10))
print my_dict
{1: 'foo', 2: 'foo', 3: 'foo', 4: 'foo', 5: 'foo', 6: 'foo', 7: 'foo', 8: 'foo', 9: 'foo'}

为了添加新元素,您可以使用以下方式更新字典:
my_dict.update(new_elements) 

有趣。不过你之后要怎样将其他键附加到这个现有的'my_dict'中呢? - asteri
只要您不需要准确地了解 len() 等事物的含义,并且在最初编写后不需要更新值,那么这可能是可以的。 - Silas Ray

2

这个怎么样:

def fancy_dict(*args):
    'Pass in a list of tuples, which will be key/value pairs'
    ret = {}
    for k,v in args:
        for i in k:
            ret[i] = v
    return ret

那么,您可以:

>>> dic = fancy_dict((range(10), 'hello'), (range(100,125), 'bye'))
>>> dic[1]
'hello'
>>> dic[9]
'hello'
>>> dic[100]
'bye'
>>> 

您还可以在fancy_dict内部添加逻辑,以检查项目是否为字符串或可迭代项,并相应地创建字典。


很好,这个完美地运行了。谢谢你!在此过程中,你还教会了我如何在Python中使用可变参数。 :) - asteri
请注意,len(ret) 可能会给您提供不正确的值。同样重要的是,迭代 ret 不会按照排序索引或插入顺序进行,并且对于插入给定值的每个范围 ID,它都会重复值。更新和添加值也不会按您所需的方式工作。 - Silas Ray

1
如果您的“范围键”是简单的数学变换,并且每个潜在有效键都有唯一的映射,那么您可以只是对list进行子类化并覆盖__getitem____setitem__,尽管在呼叫代码中使用帮助方法或直接计算会有很好的理由(例如使index()返回某些特别有意义的东西)。
class RangeList(list):
    def __getitem__(self, index):
        return super(RangeList, self).__getitem__(index / 10 if index else 0)
    def __setitem__(self, index, value):
        super(RangeList, self).__setitem__(index / 10 if index else 0, value)

0
我将这个记录下来,或许其他人也会感兴趣:

如果你把键值设为元组,它就能正常工作: my_dict = {(1, 2, 3, 10): "foo"}

编辑:我以为你想要一个列表作为键。否则,你需要这样做:

>>> import numpy as np
>>> keys = np.arange(10,dtype=int)
>>> values = np.arange(3,13)
>>> d = dict(numpy.array([keys,values]).T)
>>> d
{0: 3, 1: 4, 2: 5, 3: 6, 4: 7, 5: 8, 6: 9, 7: 10, 8: 11, 9: 12}

你需要在自定义字典上重写getitem/setitem方法来查找键,但这样做会失去字典哈希映射的查找效率。 - Silas Ray

0

也许你可以沿着这个方向做些什么:

class my_dict(dict):
    def __getitem__(self, a):
        return dict.__getitem__(self, (a-1) / 10)
    def __setitem__(self, a, b):
        dict.__setitem__(self, (a-1) / 10, b)

dict_instance = my_dict()
dict_instance[1] = 'foo'
print dict_instance[9] # prints foo

dict_instance[17] = 'bar'
print dict_instance[12] # prints bar

这有与普通字典一样快速(O(1)),但是体积小 10 倍的优点。

如果想要打印范围,还需要覆盖 __str__ 方法,同时使用这种数据类型可以非常轻松地遍历唯一键值 :)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接