2016-12-06 8 views
1

ZOBD (Python 3.x)에서 BTrees.OOBTree.OOBTree()에 키로 개체를 저장할 수 있기를 바랍니다.ZOBD OOBTree에서 객체를 키로 사용하는 올바른 방법은 무엇입니까?

from BTrees.OOBTree import OOBTree as Btree 

class Test: 
    pass 

bt=Btree() 
t=Test() 
bt[t]=None #TypeError: Object has default comparison 

그래서, 내가 __eq__ 그 오류를 제거하기 위해 정의 될 필요가있다 어딘가에 읽을 수 있지만 그 이전의 문제를 해결하기 위해 듯하지만 것 같습니다 : 오류의 예 I 시도 (주석 참조) 때 얻을 더 많은 문제를 일으킬 수 있습니다. 예 :

[편집 : OOBTree (및 TreeSet) 상속에 대한 몇 가지 문제점을 발견했습니다. 명백하게, 그들은 제대로 저장하지 않는다; 그래서, 그들이 영구 상속에도 불구하고, 영구 상속과 동일하지 않습니다.]

from BTrees.OOBTree import OOBTree as Btree 

class Test: 
    def __eq__(self, other): #Maybe this isn't the way to define the method 
     return self==other 

bt=Btree() 
t=Test() 
bt[t]=None 

t in bt #TypeError: unorderable types: Test() < Test() 

BTREE 또는 OOBTree의 키로서 객체를 사용하는 올바른 방법은 무엇입니까? 열쇠가 존재하는지 테스트 할 필요가 있습니다.

모르는 분들을 위해, ZODB의 BTrees는 영속성을 위해 설계된 확장 성있는 Python 사전 (일반 Python 사전보다 많은 키 - 값 쌍으로 실행 가능해야 함)과 매우 비슷합니다.

답변

3

나는 this answer이 문제를 해결할 수 있다고 생각합니다.

Bascically, 당신은 개체에 대한 세 가지 방법을 reimplent해야 :

  1. __eq__ (평등 검사)
  2. __ne__ (비 평등 검사)
  3. __hash__하는 사전 키와 객체가 정말 직렬화 수 있도록
+0

보통 그냥 HTTPS [total_ordering] (갈 좋은 생각을 .org/3/library/functools.html # functools.total_ordering) – dom0

0

엘리엇 베리 얼 (Eliot Berriot)의 대답이 필자에게 필요한 답변으로 안내되었지만, 다른 사람들이 물건을 찾아내는 데 더 많은 시간을 할애 할 필요가 없도록 도와 줬습니다. (필자는 두 번째 사람으로 자신에게 말하는거야.)


모든

먼저 상속하지 않습니다 (난 정말 그것에 대해 물어 보지 않았다, 그러나 당신이 유혹 할 수있는 뭔가) OOBTree 또는 OOTreeSet (이로 인해 문제가 발생 함). Persistent를 상속받은 클래스를 만들고 상속 된 OOBTree와 같은 것을 원한다면 OOBTree 또는 OOTreeSet을 내부에 두십시오 (원하는 경우 사전이나 집합처럼 보이게하는 데 필요한 메서드를 정의 할 수도 있습니다).

다음으로 ZOBD가 유지할 수있는 고유 한 정수가 없으면 객체로 인해 OOBTree와 OOTreeSets가 오작동하기 때문에 OOBTree 또는 OOTreeSet에 넣는 모든 객체에 대해 영구 ID 시스템을 만들어야합니다 Eliot이 언급 한 메소드와 다른 유사한 메소드를 정의 할 필요가 있습니다 (객체가 아닌 정수 ID를 비교할 필요가 있습니다). 즉, 객체를 생성하는 클래스의 메소드를 정의하십시오. OOBTree의 키 또는 포함 된 OOTreeSet에서됩니다. __eq__, __ne__, __hash__, __lt__, __le__, __gt____ge__ 그러나, 영구적 인 ID를 위해, 당신은 ID 카운터 클래스를 만들해야 할거야 또는 뭔가 (왜냐하면 내가 틀린 경우가 아니라면 이상한 이유로 OOBTree의 값으로 일반 정수를 저장하지 않을 것입니다. 그리고 카운터 클래스도 ID를 가져야합니다.

다음으로 객체 키를 만드는 경우 동일한 OOBTree에서 문자열과 같은 항목을 키로 만들지 않는 것이 좋으며 그렇지 않으면 신비한 문제가 발생할 수 있습니다 객체와 같은 종류의 ID 시스템이없는 문자열). 비교할 수 없기 때문에 문자열 키와 개체 키를 비교하여 오류가 발생합니다.

다음은 OOBTree에서 객체를 키로 사용할 수있는 Python 3.x 코드의 작동 예제이며, OOBTree의 영구 객체를 반복하여 키로 사용할 수 있습니다. 또한 개체를 저장하고로드하는 방법을 보여줍니다.

죄송 그것은 오랜 종류의, 그러나 당신이이 일을 할 수있는 방법의 좋은 아이디어를 제공해야합니다 : //docs.python :

import transaction, ZODB, ZODB.FileStorage 
from persistent import Persistent 
from BTrees.OOBTree import OOBTree as OOBTree 
from BTrees.OOBTree import OOTreeSet as OOTreeSet 

class Btree(Persistent): 
    def __init__(self, ID=None, **attr): 
     #I like to use entirely uppercase variables to represent ones you aren't supposed to access outside of the class (because it doesn't have the restrictions that adding _ and __ to the beginning do, and because you don't really need all caps for constants in Python) 
     Persistent.__init__(self) 
     self.DS=OOBTree() #DS stands for data structure 
     self.DS.update(attr) 
     if ID==None: 
      self.ID=-1 #To give each object a unique id. The value, -1, is replaced. 
      self.ID_SET=False 
     else: 
      self.ID=ID #You should remember what you’re putting here, and it should be negative. 
      self.ID_SET=True 
    def clear(self): 
     self.DS.clear() 
    def __delitem__(self, key): 
     del self.DS[key] 
    def __getitem__(self, key): 
     return self.DS[key] 
    def __len__(self): 
     return len(self.DS) 
    def __iadd__(self, other): 
     self.DS.update(other) 
    def __isub__(self, other): 
     for x in other: 
      try: 
       del self.DS[x] 
      except KeyError: 
       pass 
    def __contains__(self, key): 
     return self.DS.has_key(key) 
    def __setitem__(self, key, value): 
     self.DS[key]=value 
    def __iter__(self): 
     return iter(self.DS) 
    def __eq__(self, other): 
     return self.id==other.id 
    def __ne__(self, other): 
     return self.id!=other.id 
    def __hash__(self): 
     return self.id 
    def __lt__(self, other): 
     return self.id<other.id 
    def __le__(self, other): 
     return self.id<=other.id 
    def __gt__(self, other): 
     return self.id>other.id 
    def __ge__(self, other): 
     return self.id>=other.id 
    @property 
    def id(self): 
     if self.ID_SET==False: 
      print("Warning. self.id_set is False. You are accessing an id that has not been set.") 
     return self.ID 
    @id.setter 
    def id(self, num): 
     if self.ID_SET==True: 
      raise ValueError("Once set, the id value may not be changed.") 
     else: 
      self.ID=num 
      self.ID_SET=True 
    def save(self, manager, commit=True): 
     if self.ID_SET==False: 
      self.id=manager.inc() 
     manager.root.other_set.add(self) 
     if commit==True: 
      transaction.commit() 

class Set(Persistent): 
    def __init__(self, ID=None, *items): 
     Persistent.__init__(self) 
     self.DS=OOTreeSet() 
     if ID==None: 
      self.ID=-1 #To give each object a unique id. The value, -1, is replaced automatically when saved by the project for the first time (which should be done right after the object is created). 
      self.ID_SET=False 
     else: 
      if ID>=0: 
       raise ValueError("Manual values should be negative.") 
      self.ID=ID #You should remember what you’re putting here, and it should be negative. 
      self.ID_SET=True 
     self.update(items) 
    def update(self, items): 
     self.DS.update(items) 
    def add(self, *items): 
     self.DS.update(items) 
    def remove(self, *items): 
     for x in items: 
      self.DS.remove(x) 
    def has(self, *items): 
     for x in items: 
      if not self.DS.has_key(x): 
       return False 
     return True 
    def __len__(self): 
     return len(self.DS) 
    def __iadd__(self, other): 
     self.DS.update(other) 
    def __isub__(self, other): 
     self.remove(*other) 
    def __contains__(self, other): 
     return self.DS.has_key(other) 
    def __iter__(self): 
     return iter(self.DS) 
    def __eq__(self, other): 
     return self.id==other.id 
    def __ne__(self, other): 
     return self.id!=other.id 
    def __hash__(self): 
     return self.id 
    def __lt__(self, other): 
     return self.id<other.id 
    def __le__(self, other): 
     return self.id<=other.id 
    def __gt__(self, other): 
     return self.id>other.id 
    def __ge__(self, other): 
     return self.id>=other.id 
    @property 
    def id(self): 
     if self.ID_SET==False: 
      print("Warning. self.id_set is False. You are accessing an id that has not been set.") 
     return self.ID 
    @id.setter 
    def id(self, num): 
     if self.ID_SET==True: 
      raise ValueError("Once set, the id value may not be changed.") 
     else: 
      self.ID=num 
      self.ID_SET=True 
    def save(self, manager, commit=True): 
     if self.ID_SET==False: 
      self.id=manager.inc() 
     manager.root.other_set.add(self) 
     if commit==True: 
      transaction.commit() 

class Counter(Persistent): 
    #This is for creating a persistent id count object (using a plain integer outside of a class doesn't seem to work). 
    def __init__(self, value=0): 
     self.value=value 
     self.ID_SET=False 
     self.id=value 
    #The following methods are so it will fit fine in a BTree (they don't have anything to do with self.value) 
    def __eq__(self, other): 
     return self.id==other.id 
    def __ne__(self, other): 
     return self.id!=other.id 
    def __hash__(self): 
     return self.id 
    def __lt__(self, other): 
     return self.id<other.id 
    def __le__(self, other): 
     return self.id<=other.id 
    def __gt__(self, other): 
     return self.id>other.id 
    def __ge__(self, other): 
     return self.id>=other.id 
    @property 
    def id(self): 
     if self.ID_SET==False: 
      print("Warning. self.id_set is False. You are accessing an id that has not been set.") 
     return self.ID 
    @id.setter 
    def id(self, num): 
     if self.ID_SET==True: 
      raise ValueError("Once set, the id value may not be changed.") 
     else: 
      self.ID=num 
      self.ID_SET=True 

class Manager: 
    def __init__(self, filepath): 
     self.filepath=filepath 
     self.storage = ZODB.FileStorage.FileStorage(filepath) 
     self.db = ZODB.DB(self.storage) 
     self.conn = self.db.open() 
     self.root = self.conn.root 
     print("Database opened.\n") 
     try: 
      self.root.other_dict #This holds arbitrary stuff, like the Counter. String keys. 
     except AttributeError: 
      self.root.other_dict=OOBTree() 
      self.root.other_dict["id_count"]=Counter() 
     try: 
      self.root.other_set #set other 
     except AttributeError: 
      self.root.other_set=OOTreeSet() #This holds all our Btree and Set objects (they are put here when saved to help them be persistent). 
    def inc(self): #This increments our Counter and returns the new value to become the integer id of a new object. 
     self.root.other_dict["id_count"].value+=1 
     return self.root.other_dict["id_count"].value 
    def close(self): 
     self.db.pack() 
     self.db.close() 
     print("\nDatabase closed.") 

class Btree2(Btree): 
    #To prove that we can inherit our own classes we created that inherit Persistent (but inheriting OOBTree or OOTreeSet causes issues) 
    def __init__(self, ID=None, **attr): 
     Btree.__init__(self, ID, **attr) 




m=Manager("/path/to/database/test.fs") 

try: 
    m.root.tree #Causes an AttributeError if this is the first time you ran the program, because it doesn't exist. 
    print("OOBTree loaded.") 
except AttributeError: 
    print("Creating OOBTree.") 
    m.root.tree=OOBTree() 
    for i in range(5): 
     key=Btree2() 
     key.save(m, commit=False) #Saving without committing adds it to the manager's OOBTree and gives it an integer ID. This needs to be done right after creating an object (whether or not you commit). 
     value=Btree2() 
     value.save(m, commit=False) 
     m.root.tree[key]=value #Assigning key and value (which are both objects) to the OOBTree 
    transaction.commit() #Commit the transactions 

try: 
    m.root.set 
    print("OOTreeSet loaded.") 
except AttributeError: 
    print("Creating OOTreeSet") 
    m.root.set=OOTreeSet() 
    for i in range(5): 
     item=Set() 
     item.save(m, commit=False) 
     m.root.set.add(item) 
    transaction.commit() 

#Doing the same with an OOTreeSet (since objects in them suffered from the same problem as objects as keys in an OOBTree) 
for x in m.root.tree: 
    print("Key: "+str(x.id)) 
    print("Value: "+str(m.root.tree[x].id)) 
    if x in m.root.tree: 
     print("Comparison works for "+str(x.id)) 

print("\nOn to OOTreeSet.\n") 

for x in m.root.set: 
    if x in m.root.set: 
     print("Comparison works for "+str(x.id)) 

m.close()