【问题标题】:Why can't I inherit from dict AND Exception in Python?为什么我不能从 Python 中的 dict AND Exception 继承?
【发布时间】:2010-09-23 11:22:46
【问题描述】:

我有以下课程:

class ConstraintFailureSet(dict, Exception) :
    """
        Container for constraint failures. It act as a constraint failure itself
        but can contain other constraint failures that can be accessed with a dict syntax.
    """

    def __init__(self, **failures) :
        dict.__init__(self, failures)
        Exception.__init__(self)

print isinstance(ConstraintFailureSet(), Exception)
True
raise ConstraintFailureSet()
TypeError: exceptions must be classes, instances, or strings (deprecated), not ConstraintFailureSet

什么鬼?

最糟糕的是我无法尝试 super(),因为 Exception 是基于旧的类...

编辑:是的,我尝试切换继承/初始化的顺序。

EDIT2:我在 Ubuntu8.10 上使用 CPython 2.4。你更新知道这种信息很有用;-)。不管怎样,这个小谜语已经让我的三个同事闭嘴了。你会是我今天最好的朋友...

【问题讨论】:

    标签: python multiple-inheritance


    【解决方案1】:

    Exceptiondict 都是用 C 实现的。

    我认为您可以通过以下方式进行测试:

    >>> class C(object): pass
    ...
    >>> '__module__' in C.__dict__
    True
    >>> '__module__' in dict.__dict__
    False
    >>> '__module__' in Exception.__dict__
    False
    

    由于Exceptiondict 对如何在内部存储数据有不同的想法,因此它们不兼容,因此您不能同时从两者继承。

    在更高版本的 Python 中,您应该在尝试定义类时得到一个异常:

    >>> class foo(dict, Exception):
    ...     pass
    ... 
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: Error when calling the metaclass bases
        multiple bases have instance lay-out conflict
    

    【讨论】:

      【解决方案2】:

      这是怎么回事?

      class ConstraintFailure( Exception ):
          def __init__( self, **failures ):
              self.failures= failures # already a dict, don't need to do anything
          def __getitem__( self, key ):
              return self.failures.get(key)
      

      这是一个异常,它在名为 failures 的内部字典中包含其他异常。

      您能否更新您的问题以列出一些无法做到的具体事情?

      try:
          raise ConstraintFailure( x=ValueError, y=Exception )
      except ConstraintFailure, e:
          print e['x']
          print e['y']
      
      
      <type 'exceptions.ValueError'>
      <type 'exceptions.Exception'>
      

      【讨论】:

      • 您的解决方案意味着我必须使用魔术方法(setitem、items、iteritems、len 等)重写整个字典界面,这意味着手动添加十几个我也必须进行单元测试的方法。
      • 您真的在使用所有字典功能吗?为什么不能使用 e.failures 显式引用附加的字典?
      • 我至少需要列出/迭代键、值、len 和 get.setitem 的功能。但不要打扰,UserDict 会这样做,因为我不认为这段代码是一个瓶颈......无论如何感谢你的提议:-)
      • 那么,你需要五个方法:keys、values、items和setitem?我不清楚您要做什么以及“数十个”可能包含什么。您有其他方法或其他功能吗?
      【解决方案3】:

      没有原因,只有解决方案

      目前我仍然不知道原因,但我使用UserDict.UserDict 绕过它。因为是纯Python所以速度比较慢,但是我不认为在应用程序的这部分会很麻烦。

      仍然对答案感兴趣;-)

      【讨论】:

      • GvR 在他的“Unifying types and classes in Python 2.2”中警告了这一点,顺便说一句(参见python.org/download/releases/2.2.2/descrintro"You can use multiple inheritance, but you can't multiply inherit from different built-in types (for example, you can't create a type that inherits from both the built-in dict and list types)."
      【解决方案4】:

      什么版本的 Python?

      在 2.5.1 中,我什至无法定义继承自 dictException 的类:

      >>> class foo(dict, Exception):
      ...   pass
      ... 
      Traceback (most recent call last):
        File "<stdin>", line 1, in <module>
      TypeError: Error when calling the metaclass bases
          multiple bases have instance lay-out conflict
      

      如果您使用的是旧版本,则可能在类型定义期间不会进行此检查,并且冲突会导致以后出现异常。

      【讨论】:

        【解决方案5】:

        我几乎可以肯定 2.4 的问题是由旧样式类的异常引起的。

        $ python2.4
        Python 2.4.4 (#1, Feb 19 2009, 09:13:34)
        >>> type(dict)
        <type 'type'>
        >>> type(Exception)
        <type 'classobj'>
        >>> type(Exception())
        <type 'instance'>
        
        $ python2.5
        Python 2.5.4 (r254:67916, Feb 17 2009, 23:11:16)
        >>> type(Exception)
        <type 'type'>
        >>> type(Exception())
        <type 'exceptions.Exception'>
        

        在这两个版本中,如消息所述,异常可以是类、实例(旧式类)或字符串(已弃用)。

        从 2.5 版本开始,异常层次结构最终基于新的样式类。现在也允许从 BaseException 继承的新样式类的实例。 但是在 2.4 中从 Exception(旧样式类)和 dict(新样式类)的多重继承 导致新样式类是不允许的例外(混合旧样式类和新样式类可能很糟糕)。

        【讨论】:

        • 如果你说的对,我们应该可以继承V3中的Dict和Exception?
        • 正如 Dave Costa 在 Python 2.5 中提到的 Exception 和 dict 不能在相同的继承层次结构中,因为它们在 C 中实现并且存储方式不同。即使在以前的版本中,这也适用于任何内置(新样式类)类型。 Python 2.6 和 3 在这个话题上似乎没有任何改变docs.python.org/3.0/whatsnew/3.0.html
        【解决方案6】:

        使用collections.UserDict 避免元类冲突:

        class ConstraintFailureSet(coll.UserDict, Exception):
                """
                    Container for constraint failures. It act as a constraint failure itself
                    but can contain other constraint failures that can be accessed with a dict syntax.
                """
        
                def __init__(self, **failures) :
                    coll.UserDict.__init__(self, failures)
                    Exception.__init__(self)
        
        
        print( isinstance(ConstraintFailureSet(), Exception)) #True
        raise ConstraintFailureSet()
        

        【讨论】:

          猜你喜欢
          • 2020-07-28
          • 2010-09-28
          • 1970-01-01
          • 1970-01-01
          • 2010-10-20
          • 1970-01-01
          • 1970-01-01
          • 2014-09-09
          • 2017-03-30
          相关资源
          最近更新 更多