【问题标题】:Make a dictionary with duplicate keys in Python在 Python 中创建一个包含重复键的字典
【发布时间】:2022-12-04 15:23:49
【问题描述】:

我有以下列表,其中包含具有不同值的重复汽车登记号。我想把它转换成一个字典,接受这个汽车登记号码的多个键。

到目前为止,当我尝试将列表转换为字典时,它消除了其中一个键。如何制作带有重复键的字典?

名单是:

EDF768, Bill Meyer, 2456, Vet_Parking
TY5678, Jane Miller, 8987, AgHort_Parking
GEF123, Jill Black, 3456, Creche_Parking
ABC234, Fred Greenside, 2345, AgHort_Parking
GH7682, Clara Hill, 7689, AgHort_Parking
JU9807, Jacky Blair, 7867, Vet_Parking
KLOI98, Martha Miller, 4563, Vet_Parking
ADF645, Cloe Freckle, 6789, Vet_Parking
DF7800, Jacko Frizzle, 4532, Creche_Parking
WER546, Olga Grey, 9898, Creche_Parking
HUY768, Wilbur Matty, 8912, Creche_Parking
EDF768, Jenny Meyer, 9987, Vet_Parking
TY5678, Jo King, 8987, AgHort_Parking
JU9807, Mike Green, 3212, Vet_Parking

我试过的代码是:

data_dict = {}
data_list = []

def createDictionaryModified(filename):
  path = "C:\Users\user\Desktop"
  basename = "ParkingData_Part3.txt"
  filename = path + "//" + basename
  file = open(filename)
  contents = file.read()
  print contents,"\n"
  data_list = [lines.split(",") for lines in contents.split("\n")]
  for line in data_list:
    regNumber = line[0]
    name = line[1]
    phoneExtn = line[2]
    carpark = line[3].strip()
    details = (name,phoneExtn,carpark)
    data_dict[regNumber] = details
  print data_dict,"\n"
  print data_dict.items(),"\n"
  print data_dict.values()

【问题讨论】:

  • 如果字典允许具有不同关联值的重复键,那么当您稍后查找此类键的值时,您希望检索到哪个键?

标签: python dictionary


【解决方案1】:

Python 字典不支持重复键。一种解决方法是将列表或集合存储在字典中。

实现此目的的一种简单方法是使用defaultdict

from collections import defaultdict

data_dict = defaultdict(list)

您所要做的就是更换

data_dict[regNumber] = details

data_dict[regNumber].append(details)

你会得到一个列表字典。

【讨论】:

  • 一开始我不明白这等同于自己将字典键的值声明为列表并附加到它。虽然消除了几行样板,这很好。 if not my_key in data_dict: data_dict[my_key] = list()
【解决方案2】:

您可以更改 Python 中内置类型的行为。对于您的情况,创建一个 dict 子类真的很容易,它将自动将重复值存储在同一键下的列表中:

class Dictlist(dict):
    def __setitem__(self, key, value):
        try:
            self[key]
        except KeyError:
            super(Dictlist, self).__setitem__(key, [])
        self[key].append(value)

输出示例:

>>> d = dictlist.Dictlist()
>>> d['test'] = 1
>>> d['test'] = 2
>>> d['test'] = 3
>>> d
{'test': [1, 2, 3]}
>>> d['other'] = 100
>>> d
{'test': [1, 2, 3], 'other': [100]}

【讨论】:

  • 为什么不只是 if key not in self: 而不是 try:/except KeyError:
  • 这与:'from collections import defaultdict d = defaultdict(list) d['test'].append(1) d['test'].append(2) d['test'].append(3 )' 或者我可能会遗漏一些东西吗?
【解决方案3】:

我刚刚发布了一个问题的答案,该问题随后作为该问题的副本被关闭(我认为有充分的理由),但我很惊讶地发现我提出的解决方案未包含在此处的任何答案中。

与其使用 defaultdict 或乱搞成员资格测试或手动异常处理,不如使用 setdefault 方法轻松地将值附加到字典中的列表中:

results = {}                              # use a normal dictionary for our output
for k, v in some_data:                    # the keys may be duplicates
    results.setdefault(k, []).append(v)   # magic happens here!

这很像使用 defaultdict,但您不需要特殊的数据类型。当您调用 setdefault 时,它会检查第一个参数(键)是否已经在字典中。如果没有找到任何东西,它会将第二个参数(默认值,在本例中为空列表)分配为键的新值。如果密钥确实存在,则不会执行任何特殊操作(默认为未使用)。在任何一种情况下,都会返回值(无论是旧值还是新值),因此我们可以无条件地对其调用 append,知道它应该始终是一个列表。

【讨论】:

  • 我想指出为什么你应该避开.setdefault(k, []).append(v)。对于每个键值对,都会创建一个新列表。如果键不存在,则将这个新创建的列表存储在字典中,否则将其丢弃。这导致大量临时列表的创建和放弃。 defaultdict(list) 仅在密钥不存在时调用工厂方法,因此永远不会创建不需要的列表。
  • 这是一个很好的观点。 setdefault 使用的默认值确实需要预先完全实例化,然后再进行调用以查看是否确实需要它。对于空列表,性能成本很小,但并非完全微不足道。对于更重量级的对象(比如,一个大的numpy数组),它可能会让人望而却步。因此,如果性能不是很重要,那么在简化代码(已经使用普通字典)时使用此解决方案,但在创建额外对象有问题的任何情况下选择其中一个替代方案。
【解决方案4】:

你不能有一个带有重复键的字典来定义! 相反,您可以使用单个键,并将具有该键的元素列表作为值。

因此,您可以按照以下步骤操作:

  1. 查看当前元素的(初始集合的)键是否在最终字典中。如果是,转到步骤 3
  2. 用键更新字典
  3. 将新值附加到 dict[key] 列表
  4. 重复[1-3]

【讨论】:

    【解决方案5】:

    如果你想只在必要时才列出清单, 以及任何其他情况下的值,那么您可以这样做:

    class DictList(dict):
        def __setitem__(self, key, value):
            try:
                # Assumes there is a list on the key
                self[key].append(value)
            except KeyError: # If it fails, because there is no key
                super(DictList, self).__setitem__(key, value)
            except AttributeError: # If it fails because it is not a list
                super(DictList, self).__setitem__(key, [self[key], value])
    

    然后您可以执行以下操作:

    dl = DictList()
    dl['a']  = 1
    dl['b']  = 2
    dl['b'] = 3
    

    它将存储以下{'a': 1, 'b': [2, 3]}


    当我想要时,我倾向于使用这个实现反向/反向字典,在这种情况下我只是这样做:

    my_dict = {1: 'a', 2: 'b', 3: 'b'}
    rev = DictList()
    for k, v in my_dict.items():
        rev_med[v] = k
    

    这将生成与上面相同的输出:{'a': 1, 'b': [2, 3]}


    警告:此实现依赖于不存在 append 方法(在您存储的值中)。这可能会产生如果您存储的值是列表,则出现意外结果.例如,

    dl = DictList()
    dl['a']  = 1
    dl['b']  = [2]
    dl['b'] = 3
    

    会产生与 {'a': 1, 'b': [2, 3]} 之前相同的结果,但可能会出现以下结果:{'a': 1, 'b': [[2], 3]}

    【讨论】:

      【解决方案6】:

      您可以参考以下文章: http://www.wellho.net/mouth/3934_Multiple-identical-keys-in-a-Python-dict-yes-you-can-.html

      在dict中,如果一个key是一个object,就不存在重复的问题。

      例如:

      class p(object):
          def __init__(self, name):
              self.name = name
          def __repr__(self):
              return self.name
          def __str__(self):
              return self.name
      d = {p('k'): 1, p('k'): 2}
      

      【讨论】:

      • 如何使用键“k”获取所有值?这样做的唯一方法是顺序比较,这失去了使用哈希字典的意义。
      • 干净优雅的答案。谢谢!推论是,如果您已经计划使用对象,那么您不需要做任何事情。查看使用 OrderedDict 的完整示例:stackoverflow.com/a/56959984/1732392
      • @minion 就在这里。您要么存储对这些对象的引用以访问这些值,要么必须进行迭代。在前一种情况下,您还不如将数据放在关键对象中而忘记字典,在后一种情况下,您可以只使用元组列表。这并没有真正做到它在罐子上所说的那样——你只是让钥匙不同而已。这可能会解决你的问题,但在那时,字典变成了错误的数据结构,你只是有一个你不需要的间接层。
      • 好的,这个解决方案非常适用于您只需要一个 dict 而您不想通过键或其他东西检索值的情况,换句话说,除了获取数据结构之外,您不需要做任何事情。
      【解决方案7】:

      字典中不能有重复的键。使用列表字典:

      for line in data_list:
        regNumber = line[0]
        name = line[1]
        phoneExtn = line[2]
        carpark = line[3].strip()
        details = (name,phoneExtn,carpark)
        if not data_dict.has_key(regNumber):
          data_dict[regNumber] = [details]
        else:
          data_dict[regNumber].append(details)
      

      【讨论】:

      • 但是 defaultdict 解决方案比手动执行此操作更好(aix 的回答)
      • 而不是hash_key,我们可以只使用if not regNumber in data_dict
      • 是的,not in 更好,而且在 Python 3.x 中确实没有 hash_key 方法。谢谢!
      【解决方案8】:

      这是个老问题,但也许我的解决方案对某人有帮助。

      通过覆盖 __hash__ 魔术方法,您可以将相同的对象保存在字典中。

      例子:

      from random import choices
      
      class DictStr(str):
          """
              This class behave exacly like str class but
              can be duplicated in dict
          """
          def __new__(cls, value='', custom_id='', id_length=64):
              # If you want know why I use __new__ instead of __init__
              # SEE: https://stackoverflow.com/a/2673863/9917276
              obj = str.__new__(cls, value)
              if custom_id:
                  obj.id = custom_id
              else:
                  # Make a string with length of 64
                  choice_str = "abcdefghijklmopqrstuvwxyzABCDEFJHIJKLMNOPQRSTUVWXYZ1234567890"
                  obj.id = ''.join(choices(choice_str, k=id_length))
              return obj
      
          def __hash__(self) -> int:
              return self.id.__hash__()
      
      

      现在让我们创建一个字典:

      >>> a_1 = DictStr('a')
      >>> a_2 = DictStr('a')
      >>> a_3 = 'a'
      >>> a_1
      a
      >>> a_2
      a
      >>> a_1 == a_2 == a_3
      True
      >>> d = dict()
      >>> d[a_1] = 'some_data'
      >>> d[a_2] = 'other'
      >>> print(d)
      {'a': 'some_data', 'a': 'other'}
      

      笔记:此解决方案适用于任何基本数据结构,如(int、float、...)

      解释 :

      我们几乎可以使用任何对象作为 dict 类中的键(或者在其他语言中通常称为 HashMapHashTable),但是应该有一种方法来区分键,因为 dict 不知道对象。

      为此,想要作为键添加到字典中的对象必须以某种方式为自己提供一个唯一的标识符号(我将其命名为 uniq_id,它实际上是一个以某种方式使用哈希算法创建的数字)。

      由于字典结构在大多数解决方案中广泛使用, 大多数编程语言将对象 uniq_id 生成隐藏在 hash 名称构建方法中,该方法在键搜索中提供字典

      因此,如果您操纵类的 hash 方法,您可以将类的行为更改为字典键

      【讨论】:

        【解决方案9】:

        字典不支持重复键,相反你可以使用默认指令
        以下是如何使用的示例默认指令在 python3x 中解决你的问题

        from collections import defaultdict
        
        sdict = defaultdict(list)
        keys_bucket = list()
        
        data_list = [lines.split(",") for lines in contents.split("
        ")]
        for data in data_list:
            key = data.pop(0)
            detail = data
            
            keys_bucket.append(key)
            if key in keys_bucket:
                sdict[key].append(detail)
            else:
                sdict[key] = detail
        
        print("
        ", dict(sdict))
        


        上面的代码将产生如下输出:

        {'EDF768': [[' Bill Meyer', ' 2456', ' Vet_Parking'], [' Jenny Meyer', ' 9987', ' Vet_Parking']], 'TY5678': [[' Jane Miller', ' 8987', ' AgHort_Parking'], [' Jo King', ' 8987', ' AgHort_Parking']], 'GEF123': [[' Jill Black', ' 3456', ' Creche_Parking']], 'ABC234': [[' Fred Greenside', ' 2345', ' AgHort_Parking']], 'GH7682': [[' Clara Hill', ' 7689', ' AgHort_Parking']], 'JU9807': [[' Jacky Blair', ' 7867', ' Vet_Parking'], [' Mike Green', ' 3212', ' Vet_Parking']], 'KLOI98': [[' Martha Miller', ' 4563', ' Vet_Parking']], 'ADF645': [[' Cloe Freckle', ' 6789', ' Vet_Parking']], 'DF7800': [[' Jacko Frizzle', ' 4532', ' Creche_Parking']], 'WER546': [[' Olga Grey', ' 9898', ' Creche_Parking']], 'HUY768': [[' Wilbur Matty', ' 8912', ' Creche_Parking']]}
        

        【讨论】:

          猜你喜欢
          • 2012-05-26
          • 2012-03-09
          • 2011-07-10
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多