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

发布于 2024-08-18 17:34:24 字数 4934 浏览 2 评论 0原文

这里的任何人都可以确定为什么在下面所示的示例底部引发 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 <[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()

Can anyone here identify why the TypeError is being raised at the bottom of this example shown below?

>>> 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'
>>> 

Edit:

What follows is the finished version of the module started above. It is utilized in the VerseMatch program.

#! /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()

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

み格子的夏天 2024-08-25 17:34:24

该问题来自 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))

这将使类的实例可散列。

The problem is coming from threading.py, and can be reproduced more simply as follows:

>>> 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'

You can study threading.py to see exactly why thread objects need to be hashable, but the fix is easy too: just override two more methods

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

this makes your class's instances hashable.

我不在是我 2024-08-25 17:34:24

尽管亚历克斯对问题进行了准确的诊断,但我强烈认为在这种情况下(或一般情况下,就此而言,您不应该从 dict 进行多重继承。)虽然从 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.Threadthreading._Rlock 是 Python 类(这意味着它们有一个非常简单的内存布局,与 dict< /code>),但如果将来改变,或者你想包含其他类型,它就会失败。

这确实是一个坏主意。

Alex's on-point diagnosis of the problem notwithstanding, I would strongly argue that you should not multiply inherit from dict in this case (or in general, for that matter.) While it may seem convenient to subclass from it and automatically inherit all the dict behaviour, dicts (and builtin types in general) are often subject to shortcuts internally. For example, the 'get' method won't call your modified __getitem__, even when it does get the item:

>>> 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'
>>>

(And there are numerous such cases.)

Additionally, multiple inheritance involving builtin types requires that the in-memory layout of instances of all the types is compatible. It just so happens that threading.Thread and threading._Rlock are Python classes (which means they have a very simple in-memory layout that is compatible with dict) but if that were to change in the future, or you wanted to include other types, it would fail.

It is truly a bad idea.

神魇的王 2024-08-25 17:34:24

在班级级别声明以下内容是否足够且合理:

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

Would it be sufficient and reasonable to declare the following at the class level:

__hash__ = threading.Thread.__hash__
__eq__ = threading.Thread.__eq__
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文