基于属性的随机访问对象集合的Python数据结构

基于属性的随机访问对象集合的Python数据结构,python,Python,我需要一个对象集合,可以通过每个对象的某个(唯一)公共属性进行查找。现在,我正在使用一个dictionary将dictionary键分配给属性。 以下是我现在拥有的一个例子: class Item(): def __init__(self, uniq_key, title=None): self.key = uniq_key self.title = title item_instance_1 = Item("unique_key1", title="f

我需要一个对象集合,可以通过每个对象的某个(唯一)公共属性进行查找。现在,我正在使用一个dictionary将dictionary键分配给属性。 以下是我现在拥有的一个例子:

class Item():
    def __init__(self, uniq_key, title=None):
        self.key = uniq_key
        self.title = title

item_instance_1 = Item("unique_key1", title="foo")
item_instance_2 = Item("unique_key3", title="foo")
item_instance_3 = Item("unique_key2", title="foo")

item_collection = {
        item_instance_1.key: item_instance_1,
        item_instance_2.key: item_instance_2,
        item_instance_3.key: item_instance_3
        }

item_instance_1.key = "new_key"
现在这似乎是一个相当麻烦的解决方案,因为键不是对属性的引用,而是在赋值时获取键属性的值,这意味着:

  • 字典的键与已经以对象属性和
  • 更改对象属性时,字典键不会更新
使用列表并遍历对象似乎效率更低

那么,对于这个特殊的情况,是否有比dict更合适的数据结构,一个基于某个对象属性随机访问的对象集合

这需要使用Python2.4,因为这正是我(在工作中)所坚持的


如果不是很明显,我是Python新手。

嗯,dict确实是您想要的。可能麻烦的不是dict本身,而是您构建它的方式。下面是对您的示例的一个轻微增强,展示了如何使用列表表达式和dict构造函数轻松创建查找dict。这还展示了如何创建一种多重映射类型的dict,以便在给定字段值的情况下查找匹配项,该字段值可能在项之间重复:

class Item(object):
    def __init__(self, **kwargs):
        self.__dict__.update(kwargs)
    def __str__(self):
        return str(self.__dict__)
    def __repr__(self):
        return str(self)

allitems = [
    Item(key="red", title="foo"),
    Item(key="green", title="foo"),
    Item(key="blue", title="foofoo"),
    ]

# if fields are unique
itemByKey = dict([(i.key,i) for i in allitems])

# if field value can be duplicated across items
# (for Python 2.5 and higher, you could use a defaultdict from 
# the collections module)
itemsByTitle = {}
for i in allitems:
    if i.title in itemsByTitle:
        itemsByTitle[i.title].append(i)
    else:
        itemsByTitle[i.title] = [i]



print itemByKey["red"]
print itemsByTitle["foo"]
印刷品:

{'key': 'red', 'title': 'foo'}
[{'key': 'red', 'title': 'foo'}, {'key': 'green', 'title': 'foo'}]

实际上并没有像您担心的那样的信息复制:dict的key和对象的
.key
属性只是对同一对象的两个引用

唯一真正的问题是“如果
.key
被重新分配怎么办”。那么,显然您必须使用更新所有相关dict以及实例属性的属性;因此,每个对象必须知道它可能被注册的所有目录。理想情况下,人们希望使用弱引用,以避免循环依赖,但是,唉,您不能将
weakref.ref
(或代理)带到dict。因此,我在这里使用的是普通引用(替代方法不是使用
dict
实例,而是一些特殊的子类——不方便)

编辑:如果您想要一个包含项的所有实例的单个集合,那就更容易了,因为您可以将集合设置为类级属性;事实上,如果您需要的话,它可以是一个弱值字典,以避免错误地保持项目的活动状态。即:

class Item(object):

    all = weakref.WeakValueDictionary()

    def __init__(self, uniq_key, title=None):
        self._key = uniq_key
        self.title = title
        # here, if needed, you could check that the key
        # is not ALREADY present in self.all
        self.all[self._key] = self

    def adjust_key(self, newkey):
        # "key non-uniqueness" could be checked here too
        del self.all[self._key]
        self.all[newkey] = self
        self._key = newkey

    def get_key(self):
        return self._key

    key = property(get_key, adjust_key)

现在,您可以使用
Item.all['akey']
Item.all.get('akey')
for akey in Item.all:
,等等--dicts的所有丰富功能。

在这里您可以做很多很棒的事情。一个例子是让班级记录所有事情:

class Item():
    _member_dict = {}
    @classmethod
    def get_by_key(cls,key):
        return cls._member_dict[key]
    def __init__(self, uniq_key, title=None):
        self.key = uniq_key
        self.__class__._member_dict[key] = self
        self.title = title

>>> i = Item('foo')
>>> i == Item.get_by_key('foo')
True

注意:您将保留更新问题:如果
更改,则
\u成员dict
将不同步。这就是封装派上用场的地方:在不更新字典的情况下(实际上)不可能更改
键。有关如何执行此操作的良好教程,请参见。

编辑以更正我遇到的问题-这是由于我的“collection=dict()”默认参数
(*bonk*)。
现在,对函数的每次调用都将返回一个具有其预期集合的类-这是为了方便起见,以防需要多个此类集合。另外,我将集合放入类中,并返回类,而不是像以前那样在元组中分别返回两个类。(将默认容器保留为dict(),但可以将其更改为Alex的WeakValueDictionary,这当然非常酷。)

下面是Python 2.5.2中的输出:

unique_key1 foo1
unique_key2 foo2
unique_key3 foo3
****
new_unique_key foo1
unique_key2 foo2
unique_key3 foo3

在这种情况下,您还可以定义new,以防止使用已使用的键创建项目。如何用双下划线围绕单词,而不让stackoverflow将其解释为粗体?@Paul,在单词周围使用
反引号
,是否希望保留下划线。是的,项的所有实例都需要在一个集合中。酷,这看起来是一个优雅的解决方案。
def make_item_collection(container = None):
    ''' Create a class designed to be collected in a specific collection. '''
    container = dict() if container is None else container
    class CollectedItem(object):
        collection = container
        def __init__(self, key, title=None):
            self.key = key
            CollectedItem.collection[key] = self
            self.title = title
        def update_key(self, new_key):
            CollectedItem.collection[
                new_key] = CollectedItem.collection.pop(self.key)
            self.key = new_key
    return CollectedItem

# Usage Demo...

Item = make_item_collection()
my_collection = Item.collection

item_instance_1 = Item("unique_key1", title="foo1")
item_instance_2 = Item("unique_key2", title="foo2")
item_instance_3 = Item("unique_key3", title="foo3")

for k,v in my_collection.iteritems():
    print k, v.title

item_instance_1.update_key("new_unique_key")

print '****'
for k,v in my_collection.iteritems():
    print k, v.title
unique_key1 foo1
unique_key2 foo2
unique_key3 foo3
****
new_unique_key foo1
unique_key2 foo2
unique_key3 foo3