Python列表类,用于索引其对象元素的属性

时间:2012-10-28 17:05:42

标签: python list indexing

我正在寻找在python中创建列表的最佳方法,该列表为放入列表的对象的所有属性创建散列索引(dicts)。

>>> foo = IndexingList([{ 'id': 1, 'name': 'cat' }, { 'id': 2, 'name': 'dog' }])
>>> foo[0]
{'id': 1, 'name': 'cat'}

>>> foo.findall('id', 2)
[{'id': 2, 'name': 'dog'}]

>>> foo += {'id': 3, 'name': 'dog'}
>>> foo.findall('name', 'dog')
[{'id': 2, 'name': 'dog'}, {'id': 3, 'name': 'dog'}]

我认为IndexingList的数据结构如下所示:

{
    'items': [
        { 'id': 1, 'name': 'cat' }, 
        { 'id': 2, 'name': 'dog' }
    ],
    'indexes': {
        'id': {
            1: [{ 'id': 1, 'name': 'cat' }],
            2: [{ 'id': 2, 'name': 'dog' }]
        },
        'name': {
            'cat': [{ 'id': 1, 'name': 'cat' }],
            'dog': [
                { 'id': 2, 'name': 'dog' },
                { 'id': 3, 'name': 'dog' }
            ]
        }
    }
}

“索引”节点中的对象引用“项目”中的相同对象。

我认为属于自身对象的属性值可以通过使用str(property)来获取“index”中的内容来获得唯一的索引键。

2 个答案:

答案 0 :(得分:3)

使用某些collections.defaultdict()实际上很容易做到 - 尽管如果你经常使用它,你可能会考虑使用实际的数据库。

from collections import defaultdict
from functools import partial

class IndexingList:
    def __init__(self, items):
        self.items = []
        self.indices = defaultdict(partial(defaultdict, list))
        self.extend(items)

    def append(self, item):
        try:
            for index, value in item.items():
                self.indices[index][value].append(item)
        except AttributeError as e:
            raise ValueError("All children of an IndexingList must be "
                             "dict-like. '{0}' is not.".format(item)) from e
        self.items.append(item)

    def extend(self, iterable):
        for item in iterable:
            self.append(item)

    def __iadd__(self, other):
        self.extend(other)
        return self

    def __getitem__(self, item):
        return self.items[item]

    def __setitem__(self, item, value):
        self.items[item] = value

    def __delitem__(self, item):
        del self.items[item]
        for index, value in item.items():
            self.indices[index][value].remove(item)

    def find_all(self, index, value):
        return self.indices[index][value]

    def __repr__(self):
        return repr(self.items)

像这样使用:

>>> foo = IndexingList([{ 'id': 1, 'name': 'cat' }, { 'id': 2, 'name': 'dog' }])
>>> foo[0]
{'id': 1, 'name': 'cat'}
>>> foo.find_all("id", 2)
[{'id': 2, 'name': 'dog'}]
>>> foo += [{'id': 3, 'name': 'dog'}]
>>> foo.find_all('name', 'dog')
[{'id': 2, 'name': 'dog'}, {'id': 3, 'name': 'dog'}]

答案 1 :(得分:0)

我必须说Lattyware提供了一个非常好的解决方案。我仍然提供自己快速而肮脏的方法,就像在索引独特物品时它是一个简单的单线。我有时在某个列上创建一个索引,而不是构建一个好的包装容器:

my_list = [('aap', 123), ('noot', 234), ('mies', 345), ('mies', 456)]

如果该列中的键是唯一的,我们不会将任何新元素添加到列表中,也不会修改我们可能使用的索引值:

def mk_unique_index(data, col):
  g = ((elem[col], elem) for elem in data)
  return dict(g)

所以我们可以像:

一样使用它
>>> idx = mk_unique_index(my_list, 1)
>>> idx[123]
('aap', 123)

但是,如果我们希望在第0列上编制索引,我们必须使用defaultdict

from collections import defaultdict
def mk_index(data, col):
  d = defaultdict(list)
  for elem in data:
    d[elem[col]].append(elem)
  return d

用法:

>>> idx = mk_index(my_list, 0)
>>> idx['mies']
[('mies', 345), ('mies', 456)]

如果您使用字典甚至命名元组而不是元组(如果所有元素都包含您要编入索引的字段),则可以只提供列的字段名称 显然,人们也可以选择在内存中使用临时sqlite数据库。