代码之家  ›  专栏  ›  技术社区  ›  Noctis Skytower

Python中的多重继承(特定于问题)

  •  0
  • Noctis Skytower  · 技术社区  · 16 年前

    这里的任何人都能确定为什么在下面这个示例的底部出现TypeError吗?

    >>> 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 <Noctis.Skytower@gmail.com>'
    __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()
    

    2 回复  |  直到 16 年前
        1
  •  3
  •   Alex Martelli    16 年前

    问题来自于 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'
    

    线程.py 为什么? 线程对象需要是可散列的,但修复也很容易:只需重写另外两个方法

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

    这使得类的实例可以散列。

        2
  •  1
  •   Thomas Wouters    16 年前

    dict 在这种情况下(或一般情况下),虽然从中生成子类并自动继承所有dict行为似乎很方便,但dict(以及一般的内置类型)通常在内部受制于快捷方式。例如,“get”方法不会调用修改的 __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.Thread threading._Rlock )但是,如果将来这种情况发生变化,或者您希望包括其他类型,那么它将失败。

        3
  •  0
  •   Morten Lind    5 年前

    在班级层面声明以下内容是否充分合理:

    __hash__ = threading.Thread.__hash__
    __eq__ = threading.Thread.__eq__