【问题标题】:Is there a way to have a dictionary key be a range?有没有办法让字典键成为一个范围?
【发布时间】:2013-08-31 17:09:42
【问题描述】:

如果这很明显,请原谅我,但我对 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 总有一种让我感到惊讶的方式。那么有没有人知道 Python 的魔法来完成这项工作?

【问题讨论】:

  • this 你可能会感兴趣

标签: python dictionary key


【解决方案1】:

我必须说我从来没有需要做这样的事情,当然也没有内置的数据结构。 (如果你对哈希有所了解,你就会明白为什么 dict 不能那样工作。)

一种可能性是根本不使用字典,而是有单独的键和值列表,键列表是每个“范围”的开始。所以:

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-10, 12-20) 这样的空白呢?
【解决方案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 中使用可变参数。 :)
  • 请注意len(ret) 会给您提供可能不正确的值。此外,可能更重要的是,迭代ret 将不会按排序索引或插入顺序进行,并且会重复为给定值插入的每个范围 id 的值。更新和添加值也不会像您希望的那样工作。
【解决方案3】:

这当然不是常见的情况,我建议使用明显的解决方案:

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
  • 只要你不想要像len()这样的东西的准确含义,并且在最初编写它们之后不需要更新值,这可能就可以了。
【解决方案4】:

如果你的“范围键”是简单的数学转换,每个潜在的有效键都有唯一的映射,你可以只继承 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)

【讨论】:

    【解决方案5】:

    我保留此记录并希望其他人感兴趣:

    如果您制作键元组,它会起作用: 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}
    

    【讨论】:

    • 您必须在自定义 dict 上覆盖 getitem/setitem 才能查看键,除非这样,您当然会失去 dict hashmap 的查找效率。
    【解决方案6】:

    也许你可以按照这个思路做点什么:

    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__,你也可以使用这种数据类型非常容易地循环遍历唯一键:)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-02-27
      • 2020-03-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-06-03
      相关资源
      最近更新 更多