2016-12-06 223 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__可能需要被定義爲刪除錯誤,但儘管這似乎解決了前面的問題似乎導致更多問題。例如:

[編輯:應該指出,我已經發現了繼承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() 

什麼是使用對象爲在B樹或OOBTree鍵的正確方法是什麼?我也需要測試密鑰是否存在。

對於那些不知道的人來說,ZODB中的BTrees非常像可擴展的Python字典(它們應該可以使用比常規Python字典更多的鍵值對),這些字典是爲持久性而設計的。

回答

3

我認爲this answer可以幫助您解決問題。

Bascically,你必須reimplent你的對象有三種方法:

  1. __eq__(平等檢查)
  2. __ne__(非平等檢查)
  3. __hash__使物體真的序列化作爲字典鍵
+0

通常這是一個好主意,只是[total_ordering(HTTPS去://docs.python .org等/ 3 /庫/ functools.html#functools.total_ordering) – dom0

0

儘管Eliot Berriot的回答讓我想到了我需要的答案,但我想我會發布完整答案,幫助我,讓其他人不必花費額外的時間來計算東西。 (我將在第二個人對自己說話。)


首先,(我真的不問,但它的東西,你可能會做),不繼承OOBTree或OOTreeSet(這會導致問題)。如果你想要類似於繼承的OOBTree的東西(也可以定義使它看起來像字典或集合的方法(如果你需要的話)),讓你自己的類繼承Persistent,並且放入一個OOBTree或一個OOTreeSet。

全部接下來,你需要創建一個持久ID系統(對於你把OOBTree或OOTreeSet每一個對象,因爲對象造成OOBTrees和OOTreeSets,如果你沒有一個唯一整數ZOBD可以保持發生故障你需要定義艾略特所提及的方法以及其他一些相似的方法(這些方法需要比較整數ID而不是對象本身);也就是定義這些類的方法來產生對象將是一個OOBTree的鍵或包含在OOTreeSet:__eq____ne____hash____lt____le____gt____ge__然而,爲了有一個持續的ID,你將不得不作出一個ID計數器類或(因爲它不會在一個OOBTree中爲一些奇怪的原因保存普通的整數值,除非我做錯了),並且這個counter類也必須有一個ID。

接下來,您需要確保如果您正在製作對象鍵,那麼您最好不要在同一個OOBTree中將字符串這樣的東西當作鍵,否則您會遇到神祕的問題字符串不具有與您的對象相同類型的ID系統)。它會將字符串鍵與對象鍵進行比較,並導致錯誤,因爲它們不用於比較。

下面是Python 3.x代碼的一個工作示例,它允許您在OOBTree中將對象用作鍵,並且它將允許您遍歷OOBTree中的持久對象(並將它們用作鍵)。它還向您展示了它如何保存和加載對象。

對不起它是一種長,但它應該給你的這可怎麼工作的好主意:

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() 
相關問題