Python 从配置创建字典的字典

Python creating dictionary of dictionary from a configuration

作为内部项目的一部分,我必须解析一个dns 区域文件记录。该文件大致如下所示。

    $ORIGIN 0001.test.domain.com.
    test-qa     CNAME   test-qa.0001.test.domain.com.
    $ORIGIN test-qa.domain.com.
    unit-test01     A   192.168.0.2
    $TTL 60 ; 1 minute
    integration-test    A   192.168.0.102
    $ORIGIN dev.domain.com.
    web     A       192.168.10.10
    $TTL 300; 5 minutes
    api     A   192.168.10.13

默认ttl为3600,即以上数据,

test-qa     CNAME   test-qa.0001.test.domain.com. 

的 ttl 为 3600,因为它在任何地方都没有提到 $TTL。然而,

unit-test01     A   192.168.0.2 

的 ttl 为 3600 并且

integration-test    A   192.168.0.102

有 60 秒的 ttl。

我正在尝试根据上面的数据创建一个数据结构,我想字典是遍历这些数据的最佳方式。

我做了什么:

origin = re.compile("^$ORIGIN.*")
ttl = re.compile("^$TTL.*")
default_ttl = "$TTL 3600"
data_dict = {}
primary_key = None
value = None
for line in data_zones:
    if origin.search(line):
            line = line.replace("$ORIGIN ", "")
            primary_key = line
    elif ttl.search(line):
            default_ttl = line
    else:
            value = line
    data_dict[primary_key] = [default_ttl]
    data_dict[primary_key][default_ttl] = value

我想把它转换成字典,但是我得到了错误

TypeError: list indices must be integers, not str

我的示例数据结构需要类似于

0001.test.domain.com.: #This would be the first level Key
    ttl:3600: #This would be the second level key
        test-qa     CNAME   test-qa.0001.test.domain.com. #Value

test-qa.domain.com.: #This would be the first level Key
    ttl:3600: #This would be the second level key
        unit-test01     A   192.168.0.2 #value
    ttl:60: #This would be the second level key
        integration-test     A  192.168.0.102 #value

我做错了什么?

在我们查看问题的详细信息之前,可以清理此代码中的一些内容,以便更容易找到错误。遵循代码质量准则可以使代码更易于维护和理解,尤其是 - 变量名称应始终具有描述性,并告诉 reader 变量将代表什么。一个糟糕的变量名不会告诉 reader 它包含什么,而一个糟糕的变量名会告诉 reader 该变量包含它不包含的东西。

这里我们有一个变量叫做default_ttl。随意 reader 可能会假设此变量始终包含默认的 ttl,但在上面的代码示例中,一旦在源数据中指定了 ttl,变量就会被覆盖。

default_ttl = line

在代码顶部指定一个默认 TTL,然后在遇到新来源时将此值分配给一个名为 current_ttl 的变量可能会更清楚。

所以在文件的顶部我们会有:

DEFAULT_TTL = '$TTL 3600'

并且每次遇到新的来源时,当前的ttl都会重置为默认值。

if origin.search(line):
            line = line.replace("$ORIGIN ", "")
            current_ttl = DEFAULT_TTL

当在源数据中遇到特定的 TTL 时,可以将适当的值分配给当前 TTL:

elif ttl.search(line):
        current_ttl = line

进行此更改后,底部两行代码将变为:

data_dict[primary_key] = [current_ttl]
data_dict[primary_key][current_ttl] = value

错误的原因是您正在创建一个只包含一个条目的列表 - current_ttl,并在包含该列表的 data_dict 字典中创建一个条目。

在下一行,您从字典中取回列表,并尝试使用索引 current_ttl 访问它。 current_ttl 包含字符串,列表只能使用整数访问。解释器给你一条错误信息,简明扼要地总结了这一点!

在不对代码进行重大修改的情况下,最快的清理方法是对顶层使用 defaultdict data_dict:

from collections import defaultdict
data_dict = defaultdict(dict) 

所以现在,每当我们尝试引用 data_dict 中不存在的键时,defaultdict 对象将为我们创建一个新的空字典并插入到 data_dict 中我们试图访问的密钥。

data_dict[primary_key] = [default_ttl]

现在是多余的和不必要的,它可以从代码中完全删除,留下:

from collections import defaultdict
origin = re.compile("^$ORIGIN.*")
ttl = re.compile("^$TTL.*")
DEFAULT_TTL = "$TTL 3600"
data_dict = defaultdict(dict)
primary_key = None
value = None
for line in data_zones.split('\n'): #Split by line not character
    if origin.search(line):
                line = line.replace("$ORIGIN ", "")
                current_ttl = DEFAULT_TTL
                primary_key = line
    elif ttl.search(line):
            current_ttl = line
    else:
            value = line
    data_dict[primary_key][current_ttl] = value

错误行是

data_dict[primary_key][default_ttl] = value

因为data_dict[primary_key]是一个列表而不是字典对象。你可以通过

来解决这个问题
data_dict[primary_key] = {default_ttl: value}