覆盖派生类集合的len函数

时间:2014-01-08 16:17:35

标签: python

我正在尝试编写一个类似于一个集合的类,其中包含可以想象的每个可能的对象。这将允许我通过连续的交叉点整齐地精炼集合。例如。如果我有这个对象的构造函数,让我们称之为UniversalSet(),然后我想做类似以下的事情:

myset = UniversalSet()
for subset in subsets:
    myset = myset & subset

最终的myset对象将是set中所有subset个对象的交集。

现在我知道还有其他方法可以实现上述目标,但部分是作为一项智力练习,部分是因为我认为这有可能重用,我实施了以下内容:

class UniversalSet(set):
    """
    A set that contains every object.
    As we don't know what every object consists of, we just assume that
    adding anything doesn't change it, and testing for anything in that
    set returns true.    

    Use case is for continual refinement, e.g.:
    myset = UniversalSet()
    for subset in subsets:
        myset = myset & subset

    """
    def __and__(self,other):
        if type(other) is UniversalSet:
            return self
        elif type(other) is set:
            return other
        raise TypeError('Cannot "and" UniversalSet with non-set')

    def __contains__(self,elem):
        return True

    def __eq__(self,other):
        if type(other) is UniversalSet:
            return True
        elif type(other) is set:
            return False
        raise TypeError(
            'Cannot compare UniversalSet with non-set')

    def __ge__(self,other):
        if type(other) is UniversalSet:
            return True
        elif type(other) is set:
            return True
        raise TypeError(
            'Cannot compare UniversalSet with non-set')

    def __gt__(self,other):
        if type(other) is UniversalSet:
            return False
        elif type(other) is set:
            return True
        raise TypeError(
            'Cannot compare UniversalSet with non-set')

    def __iand__(self,other):
        self = self.__and__(other)
        return self

    def __init__(self):
        super(UniversalSet,self).__init__([])

    def __ior__(self,other):
        if type(other) is UniversalSet:
            return self
        elif type(other) is set:
            return self
        raise TypeError('Cannot "or" UniversalSet with non-set')

    def __isub__(self,other):
        raise TypeError('Cannot subtract from universal set')

    def __iter__(self):
        raise TypeError('Cannot iterate through universal set')

    def __ixor__(self,other):
        raise TypeError('Cannot ixor universal set')

    def __le__(self):
        if type(other) is UniversalSet:
            return True
        elif type(other) is set:
            return False
        raise TypeError(
            'Cannot compare UniversalSet with non-set')        

    def __len__(self):
        return self.len()

    def len(self):
        return int_inf.IntegerInfinity()

    def __lt__(self,other):
        if type(other) is UniversalSet:
            return False
        elif type(other) is set:
            return False
        raise TypeError(
            'Cannot compare UniversalSet with non-set')        

    def __repr__(self):
        return 'UniversalSet()'

    def __str__(self):
        return 'UniversalSet()'

    def __sub__(self,other):
        raise TypeError('Cannot subtract from universal set')

    def __or__(self,other):
        if type(other) is UniversalSet:
            return self
        elif type(other) is set:
            return self
        raise TypeError('Cannot or UniversalSet with non-set')

您会注意到len函数使用特殊对象,因为普通的无限对象不是int所需的类型。所以我实现了额外的IntegerInfinity对象。这是:

import numpy as np

class IntegerInfinity(int):
    def __eq__(self,other):
        if type(other) is IntegerInfinity:
            return True
        return False

    def __ge__(self,other):
        if type(other) is IntegerInfinity:
            return True
        if other == np.inf:
            return False
        elif type(other) is float or type(other) is int:
            return True
        raise TypeError(
            'Cannot compare type %r with IntegerInfinity' % type(other))

    def __gt__(self,other):
        if type(other) is IntegerInfinity:
            return False
        elif other == np.inf:
            return False
        elif type(other) is float or type(other) is int:
            return True
        raise TypeError(
            'Cannot compare type %r with IntegerInfinity' % type(other))

    def __le__(self,other):
        if type(other) is IntegerInfinity:
            return True
        if other == np.inf:
            return True
        elif type(other) is float or type(other) is int:
            return False
        raise TypeError(
            'Cannot compare type %r with IntegerInfinity' % type(other))

    def __lt__(self,other):
        if type(other) is IntegerInfinity:
            return False
        elif other == np.inf:
            return True
        elif type(other) is float or type(other) is int:
            return False
        raise TypeError(
            'Cannot compare type %r with IntegerInfinity' % type(other))

    def __init__(self):
        super(int,self).__init__(0)

    def __repr__(self):
        return 'int_inf'

    def __str__(self):
        return 'int_inf'

UniversalSet的其余部分对我来说似乎都很好用,除了当我在我的对象上调用len时,它不会给我很好的IntegerInfinity对象。相反,它给出了0,即

>>> uniset  = UniversalSet()
>>> # desired result
... IntegerInfinity()
int_inf
>>> # actual result
... len(uniset)
0

而且,这个零似乎无处不在。我在__init__ UniversalSet函数中尝试了一个不同的虚设集,我在__init__的{​​{1}}函数中尝试了一个不同的虚拟整数值,但都没有改变以上输出。

任何人都可以解释为什么会这样吗?和/或建议一个给我IntegerInfinity的解决方法?

Martijn的答案涵盖了我的想法。答案不是实现UniversalSetlen函数,而是删除__len__对象。更好的是,如果调用IntegerInfinity,我会提出错误。

1 个答案:

答案 0 :(得分:1)

使用PyInt_AsSsize_t() API functionlen()函数会将__len__方法返回int的任何内容投射:

  

首先会尝试将对象转换为PyIntObjectPyLongObject,如果它不是一个,然后将其值返回为Py_ssize_t

其中Py_ssize_t是您的体系结构指针大小(通常为64位或32位)。请参阅source for the type.slot_sq_length slot

由于您已将int子类化,但未提供__new__方法,因此在转换为0时,它具有默认值int()

您不能希望通过len()函数获取自定义类型作为返回值; len()始终返回int();你希望实现的最好的就是返回sys.maxsize

如果你的对象是不合格的,你可能不应该实现__len__ 。改为使对象成为迭代器。