【问题标题】:Updating dictionary without overwriting existing key-value pair?更新字典而不覆盖现有的键值对?
【发布时间】:2017-07-22 16:05:33
【问题描述】:

我有一个如下的输入文件,我可以从中构建我的字典

一般格式

<IP_1>
KEY_1=VALUE_1
KEY_2=VALUE_2

<IP_2>
KEY_1=VALUE_1
KEY_2=VALUE_2

例子

192.168.1.1
USER_NAME=admin
PASSWORD=admin123

192.168.1.2
USER_NAME=user
PASSWORD=user123

预期的字典应该是这样的:

>>print dictionary_of_ip
{'192.168.1.1':{'USER_NAME'='admin','PASSWORD'='admin123'},
 '192.168.1.2':{'USER_NAME'='user','PASSWORD'='user123'}}

本质上是字典中的字典

下面是我的代码:

def generate_key_value_pair(filePath, sep='='):
    dict_of_ip = {}
    slave_properties = {}
    with open(filePath, "rt") as f:
        for line in f:
            stripped_line = line.strip()
            if stripped_line and stripped_line[0].isdigit():
                #print 'Found Ip'
                ip = stripped_line
                dict_of_ip[ip] = ''
            elif stripped_line and stripped_line[0].isupper():
                #print "Found attributes")
                key_value = stripped_line.split(sep)
                key = key_value[0].strip()
                value = key_value[1].strip()
                slave_properties[key] = value
                dict_of_ip[ip] = slave_properties

    return dict_of_ip

我能够按预期获取第一个 IP 及其属性,但是来自第二个 IP 的第二组值覆盖了第一个。

>>print dict_of_ip
{'192.168.1.1': {'USER_NAME': 'user', 'PASSWORD': 'user123'},
 '192.168.1.2': {'USER_NAME': 'user', 'PASSWORD': 'user123'}}

dict_of_ip[ip] = slave_properties 导致覆盖。如何防止 '192.168.1.2' 键中的值覆盖第一个?

【问题讨论】:

  • if ip not in dict_of_ip: dict_of_ip[ip] = slave_properties 应该这样做...
  • 多字典不是更容易吗?
  • 提示:变量slave_properties 在其生命周期内引用了多少个不同的字典?
  • 所以你说:如果密钥已经存在,那么不要更新它,只创建。查看我的评论,只需测试字典中已有的键...

标签: python dictionary


【解决方案1】:

您可以选择正则表达式方式与 dict 理解相结合:

import re

string = """
192.168.1.1
USER_NAME=admin
PASSWORD=admin123

192.168.1.2
USER_NAME=user
PASSWORD=user123
"""

regex = re.compile(r"""
    ^
    (?P<ip>\d+\.\d+\.\d+\.\d+)[\n\r]
    USER_NAME=(?P<user>.+)[\r\n]
    PASSWORD=(?P<password>.+)
    """, re.MULTILINE | re.VERBOSE)

users = {match.group('ip'):
            {'USER_NAME': match.group('user'), 
            'PASSWORD': match.group('password')}
            for match in regex.finditer(string)}

print(users)
# {'192.168.1.2': {'USER_NAME': 'user', 'PASSWORD': 'user123'}, '192.168.1.1': {'USER_NAME': 'admin', 'PASSWORD': 'admin123'}}

看到它working on ideone.com。这是对应的demo on regex101.com。

【讨论】:

    【解决方案2】:

    使用切片和 dictcomp 的紧凑解决方案:

    with open("data.txt", "r") as f:
        f = [i.strip() for i in f.readlines()]
        ipdict = {ip: {'USER_NAME': user[10:], 'PASSWORD': password[9:]} 
                  for ip, user, password in zip(f[0::4], f[1::4], f[2::4])}
    

    【讨论】:

      【解决方案3】:

      只需在识别 IP 地址后将 slave_properties 的初始化移动到右侧即可。这样一来,遇到的每个问题都会开始为空(我还删除了您在dict_of_ip[ip] 那里进行的不必要的初始化)。

      from pprint import pprint
      
      def generate_key_value_pair(filePath, sep='='):
          dict_of_ip = {}
      
          with open(filePath, "rt") as f:
              for line in f:
                  line = line.strip()
                  if line and line[0].isdigit():  # ip?
                      slave_properties = {}  # initialize
                      ip = line
                  elif line and line[0].isupper():
                      key_value = line.split(sep)
                      key = key_value[0].strip()
                      value = key_value[1].strip()
                      slave_properties[key] = value
                      dict_of_ip[ip] = slave_properties
      
          return dict_of_ip
      
      result = generate_key_value_pair('ips.txt')
      pprint(result)
      

      输出:

      {'192.168.1.1': {'PASSWORD': 'admin123', 'USER_NAME': 'admin'},
       '192.168.1.2': {'PASSWORD': 'user123', 'USER_NAME': 'user'}}
      

      【讨论】:

        【解决方案4】:

        如果您使用 Python 的高性能数据类型,这将变得更容易和更高效。例如,这里是与defaultdict 相同的代码,而不是创建您自己的集合类型。

        from collections import defaultdict
        
        dict_of_ip = defaultdict(dict)   # This creates a dictionary of dictionaries for you.
        
        ip = None  
        for line in f:
            stripped_line = line.strip() 
            if stripped_line and stripped_line[0].isdigit():
                ip = stripped_line  # The value for the ip only changes if a new 
                                    # IP is detected. Otherwise the algorithm proceeds
                                    # with the older IP address to the nested dict. 
        
            elif stripped_line and stripped_line[0].isupper():
                key_value = stripped_line.split(sep)  
                key = key_value[0].strip()
                value = key_value[1].strip()
                dict_of_ip[ip][key] = value # IP set in the earlier if-loop. 
        

        更具体地说,您收到错误的原因是您正在为每个子词典编辑相同的 slave_properties 词典。因此,一个人的变化会传播到另一个人。

        【讨论】:

          【解决方案5】:

          你没有在循环中做slave_properties = {},所以当你认为你正在创建一个新的dict对象时,你指的是同一个dict对象

          slave_properties[key] = value
          dict_of_ip[ip] = slave_properties
          

          【讨论】:

            【解决方案6】:

            试试这个:

            def generate_key_value_pair(filePath, sep='='):
                dict_of_ip = {}
                with open(filePath, "rt") as f:
                    for line in f:
                        stripped_line = line.strip()
                        if stripped_line and stripped_line[0].isdigit():
                            #print 'Found Ip'
                            slave_properties = {}
                            ip = stripped_line
                            dict_of_ip[ip] = ''
                        elif stripped_line and stripped_line[0].isupper():
                            #print "Found attributes")
                            key_value = stripped_line.split(sep)
                            key = key_value[0].strip()
                            value = key_value[1].strip()
                            slave_properties[key] = value
                            dict_of_ip[ip] = slave_properties
            
                return dict_of_ip
            

            您使用相同的(修改后的)dict。我没有故意更改您的代码逻辑,只是将slave_properties = {} 更改为应该在的位置

            您甚至可以删除您的 slave_properties 并单独使用 dict

            def generate_key_value_pair(filePath, sep='='):
                dict_of_ip = {}
                with open(filePath, "rt") as f:
                    for line in f:
                        stripped_line = line.strip()
                        if stripped_line and stripped_line[0].isdigit():
                            #print 'Found Ip'
                            ip = stripped_line
                            dict_of_ip[ip] = {}
                        elif stripped_line and stripped_line[0].isupper():
                            #print "Found attributes")
                            key_value = stripped_line.split(sep)
                            key = key_value[0].strip()
                            value = key_value[1].strip()
                            dict_of_ip[ip][key] = value
            
                return dict_of_ip
            

            【讨论】:

              猜你喜欢
              • 2022-12-22
              • 1970-01-01
              • 1970-01-01
              • 2022-01-10
              • 1970-01-01
              • 2020-02-24
              • 1970-01-01
              • 1970-01-01
              • 2012-03-31
              相关资源
              最近更新 更多