关于字典:存储固定键键的最佳方法:在python中通过键访问的值数据集?

Best way to store fixed-keys key:value datasets that are accessed by keys in python?

我想要的是能够处理具有固定键集的数据集。所有键都是字符串。数据将永远不会被编辑。我知道这可以通过像这样的普通听写来实现:

1
2
3
data_a = {'key1': 'data1a', 'key2': 'data2a', 'key3': 'data3a'}
data_b = {'key1': 'data1b', 'key2': 'data2b', 'key3': 'data3b'}
data_c = {'key1': 'data1c', 'key2': 'data2c', 'key3': 'data3c'}

他们必须能够这样称呼:

1
data_a['key1'] # Returns 'data1a'

但是,这看起来是浪费内存(因为字典显然会让自己保持1/3的空间或类似的空间,同时还会多次存储键),而且创建起来也很麻烦,因为我需要在代码中反复输入相同的键。我还可能会意外地更改数据集中的某些内容。

我目前的解决方案是先将一组键存储在一个元组中,然后将数据也存储为元组。看起来是这样的:

1
2
3
4
keys = ('key1', 'key2', 'key3')
data_a = ('data1a', 'data2a', 'data3a')
data_b = ('data1b', 'data2b', 'data3b')
data_c = ('data1b', 'data2c', 'data3c')

要检索数据,我将执行以下操作:

1
data_a[keys.index('key1')] # Returns 'data1a'

然后,我了解了这个叫做命名双重的东西,它似乎可以做我需要的事情:

1
2
3
4
5
import collections
Data = collections.namedtuple('Data', ('key1', 'key2', 'key3'))
data_a = Data('data1a', 'data2a', 'data3a')
data_b = Data('data1b', 'data2b', 'data3b')
data_c = Data('data1b', 'data2c', 'data3c')

但是,似乎我不能简单地通过键调用值。相反,要通过键检索数据,我必须使用getattr,这看起来不太直观:

1
getattr(data_a,'key1') # Returns 'data1a'

我的标准是内存效率,首先是性能效率。在这三种方法中,哪一种是最好的方法?或者我错过了什么,还有一个更像Python的成语来得到我想要的?

编辑:我最近还了解了__slots__的存在,它显然对key:value对更有效地运行,同时消耗相同的(?)内存量。与此类似的实现是否是命名副本的合适替代方案?


是的,__slots__应该这样做。

1
2
3
4
5
6
7
8
9
10
class Data:
    __slots__ = ["key1","key2"]

    def __init__(self, k1, k2):
        self.key1, self.key2 = k1, k2

    def __getitem__(self, key):
        if key not in self.__slots__:
            raise KeyError("%r not found" % key)
        return getattr(self, key)

让我们试试看:

1
2
>>> Data(1, 2)["key1"]
1

key not in self.__slots__为条件的是一张健全的支票;如果没有getattr的话,__init__会很乐意为我们取来。


namedtuple似乎是正确的使用方法。如果"keys"是固定的,则不需要getattr,可以使用常规语法来检索对象的属性:

1
2
3
4
5
6
7
8
9
10
11
In [1]: %paste
import collections
Data = collections.namedtuple('Data', ('key1', 'key2', 'key3'))
data_a = Data('data1a', 'data2a', 'data3a')
data_b = Data('data1b', 'data2b', 'data3b')
data_c = Data('data1b', 'data2c', 'data3c')

## -- End pasted text --

In [2]: data_a.key1
Out[2]: 'data1a'

文档中也演示了这种用法:

1
2
3
4
5
6
7
8
9
10
11
12
>>> # Basic example
>>> Point = namedtuple('Point', ['x', 'y'])
>>> p = Point(11, y=22)     # instantiate with positional or keyword arguments
>>> p[0] + p[1]             # indexable like the plain tuple (11, 22)
33
>>> x, y = p                # unpack like a regular tuple
>>> x, y
(11, 22)
>>> p.x + p.y               # fields also accessible by name
33
>>> p                       # readable __repr__ with a name=value style
Point(x=11, y=22)

如果第二个参数(属性名)是常量,则通常不使用getattr。只有在可能发生变化时才需要:

1
2
3
4
5
In [3]: attr = input('Attribute: ')
Attribute: key3

In [4]: getattr(data_b, attr)
Out[4]: 'data3b'