2009-09-05 4 views
4

각 객체에 공통된 특정 (고유 한) 속성으로 조회 할 수있는 객체 컬렉션이 필요합니다. 지금은 속성에 사전 키를 할당하는 dicitionary를 사용하고 있습니다. 여기에 내가 지금 무엇을의 예입니다속성을 기반으로 임의 액세스 권한이있는 객체 컬렉션을위한 Python 데이터 구조

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보다 더 적합한 데이터 구조가 있습니까?

이것이 내가 (직장에서) 붙어있는 것처럼 파이썬 2.4에서 작동해야 할 것입니다.

분명하지 않은 경우, 저는 Python을 처음 사용합니다.

답변

5

는 실제로 아무 중복이 없다 이것은 또한, 딕셔너리의 multimap은 종류를 만들 항목에 걸쳐 복제 할 수있는 필드 값 주어진 항목을 일치 조회하는 방법을 보여줍니다 당신이 두려워하는 정보 : 사전의 키와 객체의 .key 속성은 정확히 같은 객체에 대한 두 개의 참조 일뿐입니다.

유일한 실제 문제는 ".key이 다시 할당되면 어떻게 될까요?"입니다. 그렇다면 인스턴스의 속성뿐만 아니라 모든 관련된 dicts를 업데이트하는 속성을 사용해야합니다. 그래서 각 객체는 그것이 등록 될 수있는 모든 딕트를 알아야한다. 순환 종속성을 피하기 위해 목적에 약한 참조를 사용하는 것이 이상적이지만, 어쨌든 weakref.ref (또는 프록시)은 사전에 가져갈 수 없습니다. 그래서 여기에서 일반적인 참조를 사용하고 있습니다. 대안은 dict 인스턴스를 사용하지 않고 일부 특수 하위 클래스 (예 : 편리하지 않음)를 사용하지 않는 것입니다.

def enregister(d, obj): 
    obj.ds.append(d) 
    d[obj.key] = obj 

class Item(object): 
    def __init__(self, uniq_key, title=None): 
     self._key = uniq_key 
     self.title = title 
     self.ds = [] 

    def adjust_key(self, newkey): 
     newds = [d for d in self.ds if self._key in d] 
     for d in newds: 
      del d[self._key] 
      d[newkey] = self 
     self.ds = newds 
     self._key = newkey 

    def get_key(self): 
     return self._key 

    key = property(get_key, adjust_key) 

편집 : 당신이 항목의 모든 인스턴스에 하나의 콜렉션을 원한다면, 그건 당신이 컬렉션 클래스 - 레벨 속성 수로도 쉽게; 실제로 그것이 필요한 경우 실수로 항목을 유지하는 것을 피하기 위해 WeakValueDictionary가 될 수 있습니다. 즉 :

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의 모든 다양한 기능을 제공합니다.

+0

예, Item의 모든 인스턴스는 하나의 컬렉션에 있어야합니다. 멋지다. 우아한 솔루션처럼 보인다. –

0

글쎄, 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'}] 
2

여기에서 할 수있는 많은 것들이 있습니다. 한 가지 예는 클래스가 모든 것을 추적 할 수 있도록하는 것입니다 :

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 

참고 업데이트 문제를 유지합니다 : key 변경의 _member_dict가 동기화 떨어지는 경우. 캡슐화가 편리해질 것입니다 : 사전을 업데이트하지 않고 key을 변경하는 것은 (사실상) 불가능하게하십시오. 이를 수행하는 방법에 대한 좋은 자습서는 this tutorial을 참조하십시오.

+0

이 경우 이미 사용 된 키가있는 Item이 생성되지 않도록 __new__을 정의 할 수도 있습니다. – PaulMcG

+0

어떻게 두 개의 밑줄로 단어를 둘러싸고 있으며 stackoverflow가 없으면이를 굵게 표시로 해석합니까? – PaulMcG

+0

@ Paul, 단어 주위에'backquotes'를 사용하면 밑줄을 그대로 보존하고 싶습니다. –

0

내 "collection = dict()"기본 매개 변수로 인한 문제를 수정하는 편집 (*bonk*). 이제는 함수를 호출 할 때마다 자체 컬렉션이있는 클래스가 반환됩니다. 그러한 컬렉션이 둘 이상 필요합니다. 또한 컬렉션을 클래스에 넣고 두 클래스 대신 클래스를 이전처럼 튜플에 개별적으로 반환합니다. (DICT() 여기 기본 컨테이너를 떠나 있지만, 물론 매우 멋진 알렉스의 WeakValueDictionary에 변경 될 수 있습니다.)

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 

을 그리고 여기 파이썬 2.5.2의 출력입니다 :

unique_key1 foo1 
unique_key2 foo2 
unique_key3 foo3 
**** 
new_unique_key foo1 
unique_key2 foo2 
unique_key3 foo3 
관련 문제