【问题标题】:Multiple keys per value每个值多个键
【发布时间】:2012-07-12 01:11:31
【问题描述】:

是否可以在 Python 字典中为每个值分配多个键。一种可能的解决方案是为每个键分配值:

dict = {'k1':'v1', 'k2':'v1', 'k3':'v1', 'k4':'v2'}

但这不是内存效率,因为我的数据文件大于 2 GB。否则,您可以制作字典键的字典:

key_dic = {'k1':'k1', 'k2':'k1', 'k3':'k1', 'k4':'k4'}
dict = {'k1':'v1', 'k4':'v2'}
main_key = key_dict['k2']
value = dict[main_key]

这也非常耗费时间和精力,因为我必须浏览整个字典/文件两次。还有其他简单且内置的 Python 解决方案吗?

注意:我的字典值不是简单的字符串(如问题 'v1'、'v2')而是复杂的对象(包含不同的其他字典/列表等,无法腌制它们)

注意:这个问题看起来类似于How can I use both a key and an index for the same dictionary value? 但我不是在寻找有序/索引字典,而是在寻找除此问题中提到的两个之外的其他有效解决方案(如果有的话)。

【问题讨论】:

  • 您不太可能找到比第一个更好的解决方案。只需将多个键放入字典中。为什么这还不够好?
  • 正如我所说的,文件非常大,因此多次放置相同的值需要大量内存。
  • python中的值不一定在python中重复,即使使用多个键来引用它们。
  • 该值不会占用额外的内存,除非您每次都重新创建它。请参阅@Useless 的答案。
  • @MartijnPieters - 哦我错过了这一点我想我现在应该做更多的测试我记得 i,j=0, 0; id(i)==id(j);真的:)

标签: python python-3.x python-2.7


【解决方案1】:

值是什么类型?

dict = {'k1':MyClass(1), 'k2':MyClass(1)}

会给出重复的值对象,但是

v1 = MyClass(1)
dict = {'k1':v1, 'k2':v1}

导致两个键都指向同一个实际对象。

在最初的问题中,您的值是字符串:即使您两次声明同一个字符串,我认为在这种情况下它们将被实习到同一个对象


注意。如果你不确定你是否有重复,你可以这样找出:

if dict['k1'] is dict['k2']:
    print("good: k1 and k2 refer to the same instance")
else:
    print("bad: k1 and k2 refer to different instances")

is 感谢 J.F.Sebastian,替换 id()

【讨论】:

  • 字典值不是简单的字符串而是复杂的对象(包含不同的其他字典/列表等)
  • 您可以使用obj1 is obj2 代替id(obj1) == id(obj2) 来测试对象身份
  • @d.putto - 在这种情况下,请确保您使用每个键存储相同的 instance,而不是具有相同内容的重复实例(根据我的前两个例子)。如果不确定,请通过is 确认
  • 类型无关紧要。你不应该依赖 Python 如何处理一些不可变对象。如果您需要相同的对象;只需提供它(如v1 示例)
  • 我同意你不应该,只是想检查一下 OP 是否已经开始自动实习。感谢is 提醒顺便说一句。
【解决方案2】:

看看这个 - 它正是你所要求的实现:multi_key_dict(ionary)

https://pypi.python.org/pypi/multi_key_dict (来源https://github.com/formiaczek/python_data_structures/tree/master/multi_key_dict

(在 Unix 平台上,它可能以软件包的形式提供,您可以尝试使用以下方式安装它:

sudo apt-get install python-multi-key-dict

对于 Debian,或您的发行版的等价物)

您可以使用不同类型的键,但也可以使用相同类型的键。您还可以使用您选择的键类型来迭代项目,例如:

m = multi_key_dict()
m['aa', 12] = 12
m['bb', 1] = 'cc and 1'
m['cc', 13] = 'something else'

print m['aa']   # will print '12'
print m[12]     # will also print '12'

# but also:
for key, value in m.iteritems(int):
    print key, ':', value
# will print:1
# 1 : cc and 1
# 12 : 12
# 13 : something else

# and iterating by string keys:
for key, value in m.iteritems(str):
    print key, ':', value
# will print:
# aa : 12
# cc : something else
# bb : cc and 1

m[12] = 20 # now update the value
print m[12]   # will print '20' (updated value)
print m['aa']   # will also print '20' (it maps to the same element)

键的数量没有限制,所以代码如下:

m['a', 3, 5, 'bb', 33] = 'something' 

是有效的,任何一个键都可以用来引用这样创建的值(读取/写入或删除它)。

编辑:从 2.0 版开始,它也应该适用于 python3。

【讨论】:

  • 如果两个键包含相同的元素,比如m['aa', 12]m[12, 'bb'],会怎样。当你这样做时会发生什么m[12] = 20
  • 如果两个键包含相同的元素(这实际上是它试图实现的) - 可以使用其中任何一个键访问该元素,并删除其中任何一个(例如 del[12] 或 @987654330 @) 将删除该元素,并且任何其他键都无法访问它(也删除了对其他键的引用)。
  • m[12] = 20 将:1)如果 m 不包含具有此键的元素(单个或多个)-它将创建一个具有键 12 的元素并将其分配(映射)到值 20 (如标准字典)。 2)如果元素存在(如上面的示例),它将更新此键引用的值。如果此值与多个键映射 - 使用这些其他键访问它将引用此(更新的)值。我已经更新了上面的示例以反映这一点。
  • 我试图通过 2to3.py 运行它,但是当我第一次点击 setitem 时,测试结果却出现了问题。我不确定是什么原因造成的,但它最终会递归调用 getitem 并最终超过最大递归深度。不幸的是,因为听起来这种字典类型可能是我要解决的问题所需要的。
【解决方案3】:

使用 python 2.7/3,您可以将元组、值对与字典理解相结合。

keys_values = ( (('k1','k2'), 0), (('k3','k4','k5'), 1) )

d = { key : value for keys, value in keys_values for key in keys }

您也可以类似地更新字典。

keys_values = ( (('k1',), int), (('k3','k4','k6'), int) )

d.update({ key : value for keys, value in keys_values for key in keys })

我认为这并不是你问题的核心,但鉴于标题,我认为这属于这里。

【讨论】:

  • 我在评论中找到了您的帖子。我对这个话题的理解不足以判断你的答案,所以我跳过了这篇评论,把它留给其他人。无论如何,让我说“我认为这并没有真正触及你问题的核心”的答案对我来说并不是一个好答案。您正在解决标题的事实是不够的。再说一次,正如我所说,我对这个话题还不够了解,所以我不会以任何方式投票。但我仍然建议改进这个答案,这样你就不必写“我认为这不是你问题的核心”。
  • 抱歉给法比奥带来了困惑,尽管我认为我们可能对成为事物的核心意味着什么存在语义上的分歧。我认为我的解决方案适合将多个键分配给一个共同的值,这确实是问题,因为它在标题和 d.putto 帖子的第一部分中提出。第二个要求,“内存效率”是我认为问题的核心。我希望这有助于验证我的回答。尽管如此,我觉得您的评论既不是原始帖子,也不是我回复的核心,因此与讨论无关。
【解决方案4】:

最直接的方法是使用 dict.fromkeys() 方法构建字典。它将一系列键和一个值作为输入,然后将值分配给每个键。
你的代码是:

dict = dict.fromkeys(['k1', 'k2', 'k3'], 'v1')
dict.update(dict.fromkeys(['k4'], 'v2'))

输出是:

print(dict)
{'k1': 'v1', 'k2': 'v1', 'k3': 'v1', 'k4': 'v2'}

【讨论】:

    【解决方案5】:

    您可以为已从解析数据创建的对象构建一个辅助字典。键是解析的数据,值是你构造的对象——比如字符串值应该被转换为某个特定的对象。这样您就可以控制何时构造新对象:

    existing = {}   # auxiliary dictionary for making the duplicates shared
    result = {}
    for k, v in parsed_data_generator():
        obj = existing.setdefault(v, MyClass(v))  # could be made more efficient
        result[k] = obj
    

    然后所有result字典重复值对象将由MyClass类的单个对象表示。构建结果后,可以删除existing辅助字典。

    这里的dict.setdefault() 可能是优雅而简短的。但是您应该稍后测试更健谈的解决方案是否效率更高——见下文。原因是MyClass(v) 总是被创建(在上面的例子中),然后如果它的重复存在则丢弃:

    existing = {}   # auxiliary dictionary for making the duplicates shared
    result = {}
    for k, v in parsed_data_generator():
        if v in existing:
            obj = existing[v]
        else:
            obj = MyClass(v)
            existing[v] = obj
    
        result[k] = obj
    

    v 未转换为任何特殊时,也可以使用此技术。例如,如果v 是一个字符串,则辅助字典中的键和值将具有相同的值。但是,字典的存在确保了对象将被共享(Python 并不总是保证)。

    【讨论】:

      【解决方案6】:

      我很惊讶没有人提到将元组与字典一起使用。这工作得很好:

      my_dictionary = {}
      my_dictionary[('k1', 'k2', 'k3')] = 'v1'
      my_dictionary[('k4')] = 'v2'
      

      【讨论】:

      • 是的,但它不能解决问题,因为你不能调用 my_dictionary['k1'] 来获取 'v1'(你会得到 KeyError,因为该值的正确键是 ('k1', 'k2', 'k3'))
      【解决方案7】:

      我能够使用 pandas MultiIndex 实现类似的功能,尽管在我的情况下这些值是标量:

      >>> import numpy
      >>> import pandas
      >>> keys = [numpy.array(['a', 'b', 'c']), numpy.array([1, 2, 3])]
      >>> df = pandas.DataFrame(['val1', 'val2', 'val3'], index=keys)
      >>> df.index.names = ['str', 'int']
      >>> df.xs('b', axis=0, level='str')
              0
      int      
      2    val2
      
      >>> df.xs(3, axis=0, level='int')
              0
      str      
      c    val3
      

      【讨论】:

        猜你喜欢
        • 2012-10-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-03-05
        • 2022-11-26
        • 2015-08-12
        相关资源
        最近更新 更多