2010-01-25 81 views
0

任何人都可以在這裏找到爲什麼TypeError會在下面顯示的示例底部出現嗎?Python中的多繼承(具體問題)

>>> import threading 
>>> class SessionManager(threading.Thread, threading._RLock, dict): 

    UPDATE = 60 * 60 

    def run(self): 
     while True: 
      time.sleep(self.UPDATE) 
      with self: 
       for key in tuple(self): 
        if not self[key]: 
         del self[key] 

    def __getitem__(self, key): 
     session = super()[key] 
     session.wakeup() 
     return session 

>>> SM = SessionManager() 
>>> SM.daemon = True 
>>> SM.start() 
Traceback (most recent call last): 
    File "<pyshell#5>", line 1, in <module> 
    SM.start() 
TypeError: unhashable type: 'SessionManager' 
>>> 

編輯:

接踵而來的是以上的啓動模塊的漢化版。它被用於VerseMatch計劃。

#! /usr/bin/env python 
"""Oversee the timely destruction of unused sessions. 

The two classes in this module allow automated memory cleanup to be regularly 
performed and timed actions to be executed within reasonable time periods.""" 

################################################################################ 

__author__ = 'Stephen "Zero" Chappell <[email protected]>' 
__date__ = '11 February 2010' 
__version__ = '$Revision: 3 $' 

################################################################################ 

import threading 
import time 

################################################################################ 

class SessionManager(threading.Thread, threading._RLock, dict): 

    """Manage session objects along with associated data. 

    This class acts as dictionary with a data-protection mutex. 
    It can run a cleanup routine at regular intervals if needed.""" 

    def __init__(self, sleep_interval): 
     """Initialize variables in SessionManager's parent classes.""" 
     threading.Thread.__init__(self) 
     threading._RLock.__init__(self) 
     self.__sleep_interval = sleep_interval 

    def run(self): 
     """Remove old sessions from memory as needed. 

     This method is executed by calling .start() on a SessionManager 
     object. The "daemon" attribute may need be set to True before 
     activating this feature. Please note that once this cleanup 
     routine begins, it must run until the program terminates.""" 
     while True: 
      time.sleep(self.__sleep_interval) 
      with self: 
       for key in tuple(self): 
        if not super().__getitem__(key): 
         del self[key] 

    def __setitem__(self, key, value): 
     """Add manager attribute to value before storing it.""" 
     value.manager = self 
     super().__setitem__(key, value) 

    def __getitem__(self, key): 
     """Retrieve the session specified by the given key. 

     Like a normal dictionary, the value is returned to the caller 
     if it was found. However, the wakeup method on the session is 
     called first. This effectively delays the session's deletion.""" 
     session = super().__getitem__(key) 
     session.wakeup() 
     return session 

    def __hash__(self): 
     """Compute a hash as required by Thread objects.""" 
     return id(self) 

################################################################################ 

class Session: 

    """Store session variables for a limited time period. 

    The only functionality this class directly supports is calling an event 
    handler when the instance is destroyed. Session objects given to a 
    SessionManager are automatically cleared out of memory when their "time to 
    live" is exceeded. The manager must be started for such functionality.""" 

    def __init__(self, time_to_live, on_destroyed=None): 
     """Initialize timeout setting and deletion handler.""" 
     self.__time_to_live = time_to_live 
     self.__on_destroyed = on_destroyed 
     self.wakeup() 

    def wakeup(self): 
     """Refresh the last-accessed time of this session object. 

     This method is automatically called by the class initializer. 
     Instances also get a wakeup call when retrieved from a manager.""" 
     self.__time = time.time() 

    def __bool__(self): 
     """Calculate liveliness of object for manager.""" 
     return time.time() - self.__time <= self.__time_to_live 

    def __del__(self): 
     """Call deletion event handler if present. 

     Completely optional: an on_destroyed handler may be specified 
     when the object is created. Exception handling is non-existent.""" 
     if self.__on_destroyed is not None: 
      self.__on_destroyed() 

+0

'不可用類型:'SessionManager'':可能你需要定義'__hash__'? – kennytm 2010-01-25 18:11:05

+0

爲什麼?我仍然不明白誰首先需要散列。 – 2010-01-25 18:13:12

+0

您不應該乘以 - 從不明確支持它的類繼承。線程和(未記錄的)_RLock不合作調用基本方法(使用'super');由兩者製成的對象將不會被正確初始化,因此可能無法按預期工作。我不知道你爲什麼試圖在這裏使用MI ...正常的路線肯定是在這裏爲其他類使用組合。 – bobince 2010-01-25 19:18:52

回答

2

問題是從threading.py來了,如下可以更簡單地轉載:

>>> import threading 
>>> class SessionManager(threading.Thread, threading._RLock, dict): pass 
... 
>>> s = SessionManager() 
>>> s.start() 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/threading.py", line 469, in start 
    _limbo[self] = self 
TypeError: unhashable type: 'SessionManager' 

你可以學習threading.py清楚地看到爲什麼線程對象必須是哈希的,但修復也很簡單:只需重寫兩種方法

def __eq__(self, other): return self is other 
def __hash__(self): return hash(id(self)) 

這使得你的類的實例可以被哈希。

+0

謝謝!我猜以下文檔是錯誤的:*用戶定義的類默認情況下有__eq __()和__hash __()方法;與他們,所有的對象比較不平等(除了自己)和x .__哈希__()返回id(x)。* – 2010-01-25 18:16:49

+1

不,文檔是正確的;但它沒有提到的是,默認'__hash__'只有在使用默認'__eq__'的情況下才有效。如果一個類重寫'__eq__',默認的'__hash__'會產生一個錯誤。 – 2010-01-25 18:54:29

1

亞歷克斯對這個問題的點上診斷儘管如此,但我強烈主張,在這種情況下(或者一般來說,在這種情況下)不應該繼承dict。儘管子類從它自動並自動繼承所有的字典行爲,字典(和一般的內置類型)通常在內部受到快捷方式的限制。例如,「獲得」的方法不會叫你修改__getitem__,甚至當它得到的資料:(並且有許多這樣的情況)

>>> class MyDict(dict): 
...  def __getitem__(self, key): 
...   print("in __getitem__(%r)" % (key,)) 
...   return super(MyDict, self).__getitem__(key) 
... 
>>> d = MyDict({'a': 'b', 'c': 'd'}) 
>>> d['a'] 
in __getitem__('a') 
'b' 
>>> d.get('c') 
'd' 
>>> 

此外,多重繼承涉及內置類型要求所有類型的實例的內存中佈局是兼容的。它恰好是threading.Threadthreading._Rlock是Python類(這意味着它們有一個非常簡單的內存中佈局,與dict兼容),但如果將來要更改,或者您想包含其他類型,它將失敗。

這確實是一個壞主意。