【问题标题】:Make a dictionary with duplicate keys in Python在 Python 中创建一个带有重复键的字典
【发布时间】:2012-05-26 17:59:33
【问题描述】:

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

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

名单是:

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】:

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

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

例子:

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 名称构建方法中,该方法在键搜索中提供 dict

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

【讨论】:

    【解决方案2】:

    字典不支持重复键,你可以使用 defaultdict
    下面是如何在python3x中使用defaultdict来解决你的问题的例子

    from collections import defaultdict
    
    sdict = defaultdict(list)
    keys_bucket = list()
    
    data_list = [lines.split(",") for lines in contents.split("\n")]
    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("\n", 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']]}
    

    【讨论】:

      【解决方案3】:

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

      在字典中,如果一个键是一个对象,则没有重复的问题。

      例如:

      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 就在这里。要么存储对这些对象的引用以访问值,要么必须进行迭代。在前一种情况下,您不妨将数据放在键对象中而忘记字典,在后一种情况下,您可以只使用元组列表。这并没有真正做到它在锡上所说的 - 你只是让钥匙不同。这可能会解决您的问题,但到那时,字典变成了错误的数据结构,您只需要一层不需要的间接层。
      • 好的,这个解决方案非常适用于您只需要一个字典并且您不想通过键或其他东西检索值的情况,换句话说,您不需要做任何事情获取数据结构。
      【解决方案4】:

      如果您希望仅在必要时拥有列表,并在任何其他情况下使用值,那么您可以这样做:

      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]}


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

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

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

      【讨论】:

        【解决方案5】:

        您可以更改 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)' 或者我可能遗漏了什么?
        【解决方案6】:

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

        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 方法。谢谢!
        【解决方案7】:

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

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

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

        【讨论】:

          【解决方案8】:

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

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

          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!
          

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

          【讨论】:

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

          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()
          猜你喜欢
          • 2022-12-04
          • 1970-01-01
          • 2019-11-30
          • 1970-01-01
          • 1970-01-01
          • 2011-04-18
          • 2016-07-09
          • 2023-03-19
          • 1970-01-01
          相关资源
          最近更新 更多