存储固定密钥的最佳方式:通过python中的密钥访问的值数据集?

问题描述:

我想要的是能够处理具有固定键集的数据集。所有的键都是字符串。数据将永远不会被编辑。我知道这与正常类型的字典来完成,像这样:存储固定密钥的最佳方式:通过python中的密钥访问的值数据集?

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

他们必须能够被称为像这样:

data_a['key1'] # Returns 'data1a' 

然而,这看起来是浪费内存(因为字典明显保持自己三分之一空或类似的东西,同时也存储密钥多次),也很繁琐的创建,因为我需要不断地在我的代码中一遍又一遍地输入相同的密钥。我也冒着意外改变数据集中的某些东西的风险。

我现在的解决方案是首先将一组键存储在一个元组中,然后将数据作为元组存储起来。它看起来像这样:

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

检索数据,我这样做:

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

然后,我了解到这似乎是能够做什么,我需要这个东西叫做namedtuples:

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,这似乎不是很直观:

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

我的标准是记忆效率,然后再执行效率。在这三种方法中,哪种方法可以做到最好?或者我错过了一些东西,还有更多的pythonic成语来得到我想要的东西?

编辑:我现在最近也了解到__slots__的存在,它显然运行更高效的键:值对,而非常消耗相同(?)的内存量。与this类似的实现是否可以替代namedtuples?

+0

一个正交的建议,你看过[pandas](http://pandas.pydata.org/)吗? – tacaswell

+0

会不会有一些像memcache那样的k/v存储会更好? –

是,__slots__应该做的:它是否会改变时才需要。

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) 

让我们尝试了这一点:

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

key not in self.__slots__的条件是一个全面的检查;如果它不存在,getattr会很乐意为我们提取__init__

namedtuple似乎是正确的使用。如果你的“钥匙”是固定的,你不需要getattr并且可以使用正常的语法检索对象的属性:

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' 

这种用法也证明文档:

>>> # 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

In [3]: attr = input('Attribute: ') 
Attribute: key3 

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

嗯,问题是要检索的值可能会有所不同,所以不是实际输入'key1',而是一个存储键(这是一个字符串)的变量。所以getattr()是必要的。可能是我的错误,不能在示例中显示。除非可以编辑namedtuple的'__getitem__'方法吗? – Eric

+0

@Eric'__getitem__'已经为'namedtuple'做了一个理智的事情,其行为与常规元组一样(参见文档中的示例)。但是你可以继承它,并调用'getattr'来代替。不过,保存你输入几个字符是一个很有争议的理由。 –

+0

与字典相比,namedtuple的内存占用量是多少? –