将文本网格转换为Python中的嵌套字典

3 投票
1 回答
1010 浏览
提问于 2025-04-17 17:32

想象一下,一个文本文件里有这样一个网格:

  A  B  C
A 0  1  2
B 3  0  5
C 6  7  0

我想把它转换成Python中的字典,像下面这样:

{
  'A': {'A': 0, 'B':3, 'C':6},
  'B': {'A': 1, 'B':0, 'C':7},
  'B': {'A': 2, 'B':5, 'C':0}
}

这样我就可以通过以下方式访问单元格:

matrix['A']['B'] # 3

我现在有一些很粗糙的代码(请不要太苛责我):

matrix = {}
f = open(filepath, 'r')
lines = f.readlines()
keys = lines[0].split()

for key in keys:
    matrix[key] = {}

for line in lines[1:]:
    chars = line.split()
    key_a = chars[0]
    for i, c in enumerate(chars[1:]):
        key_b = keys[i-1]
        matrix[key_a][key_b] = int(c)

print matrix

# Outputs {'A': {'A': 1, 'C': 0, 'B': 2}, 'C': {'A': 7, 'C': 6, 'B': 0}, 'B': {'A': 0, 'C': 3, 'B': 5}}

虽然这样做没有错,但我已经很久没用Python了,是否有更好的方法呢?也许嵌套字典并不是最好的选择?

更新:

  1. 不幸的是,我需要用纯Python来做,所以不能使用外部库(相信我,我真的很想用)
  2. 我把我的示例代码从伪代码更新成了实际代码。感到很羞愧。

1 个回答

4

你的代码看起来不错,但这里有一个替代方案:

import collections
with open('grid_file.txt', 'r') as f:
    columns = next(f).split()
    matrix = collections.defaultdict(dict)
    for line in f:
        items = line.split()
        row, vals = items[0], items[1:]
        for col, val in zip(columns, vals):
            matrix[col][row] = int(val)
print(matrix)

这个方案的结果是

defaultdict(<type 'dict'>, {'A': {'A': 0, 'C': 6, 'B': 3}, 'C': {'A': 2, 'C': 0, 'B': 5}, 'B': {'A': 1, 'C': 7, 'B': 0}})

一些建议:

  • 使用

    with open(...) as f
        ...
    

    而不是

    f = open(...)
    f.close()
    

    因为当Python离开with-block时,文件句柄会自动关闭。使用with可以确保你不会忘记关闭文件句柄,即使发生了异常,文件句柄也会在离开with-block时被关闭。

  • 一般来说,如果可以的话,最好避免使用f.readlines()。这个方法会把整个文件读入一个列表中。如果文件很大,这会占用很多内存。通常可以使用

    with open(...) as f:
        for line in f:
    

    来代替。

  • 如果你把matrix设置为collections.defaultdict(dict),那么matrix[field]默认会是一个dict。这样你就可以省去初始化的步骤:

    for key in keys:
        matrix[key] = {}
    
  • defaultdictdict的一个子类,所以你可以像使用dict一样使用它。如果你不喜欢它的打印方式,或者想要阻止matrix自动给任何key分配一个空的字典,你可以用以下方法把defaultdict转换回普通的dict

    matrix = dict(matrix)
    
  • 如果可以的话,尽量避免在for-loops中使用数字索引。

    for i, c in enumerate(chars[1:]):
    

    虽然在大多数类似C语言的语言中这是常见做法,但Python有更好的方法:直接遍历项目本身:

    for col, val in zip(columns, vals):
    

    这样代码更易读,因为它给你真正感兴趣的对象分配了一个变量名,而不仅仅是一个索引,你还得把它组合成像keys[i-1]这样的东西。这也能帮助你避免“越界”错误,因为在调整索引时可能会出现这种错误,比如在keys[i-1]中。


另一种可能是不用嵌套字典,而是用2元组(列,行)作为键:

with open('grid_file.txt', 'r') as f:
    columns = next(f).split()
    matrix = {}
    for line in f:
        items = line.split()
        row, vals = items[0], items[1:]
        for col, val in zip(columns, vals):
            matrix[col, row] = int(val)
print(matrix)

结果是

{('B', 'C'): 7, ('A', 'A'): 0, ('B', 'B'): 0, ('B', 'A'): 1, ('C', 'A'): 2, ('C', 'B'): 5, ('C', 'C'): 0, ('A', 'B'): 3, ('A', 'C'): 6}

然后你可以这样访问矩阵中的(列,行):

print(matrix['A','C'])
# 6

顺便说一下,如果你安装了pandas

import pandas as pd
import io

text = '''\
A  B  C
A 0  1  2
B 3  0  5
C 6  7  0'''

df = pd.read_table(io.BytesIO(text), sep='\s+')
print(df.to_dict())

结果是

{'A': {'A': 0, 'B': 3, 'C': 6},
 'B': {'A': 1, 'B': 0, 'C': 7},
 'C': {'A': 2, 'B': 5, 'C': 0}}

撰写回答