如何在Python中比较自定义类中None对象的相等性?

3

我正在为Python编写一个纯粹用于学习目的的Queue数据结构。这是我的class。当我比较两个Queue对象是否相等时,我会出现错误。我认为错误是由于我在__eq__中没有比较None引起的。但是我该如何检查None并相应地return。实际上,我在幕后使用list并调用其__eq__,以为它应该像这里所示那样处理,但它没有。

>>> l=[1,2,3]
>>> l2=None
>>> l==l2
False

这是我的类:
@functools.total_ordering
class Queue(Abstractstruc,Iterator):

    def __init__(self,value=[],**kwargs):
        objecttype = kwargs.get("objecttype",object)
        self.container=[]
        self.__klass=objecttype().__class__.__name__
        self.concat(value)


    def add(self, data):
        if (data.__class__.__name__==self.__klass or self.__klass=="object"): 
            self.container.append(data)
        else:
            raise Exception("wrong type being added")

    def __add__(self,other):
        return Queue(self.container + other.container)


    def __iadd__(self,other):
        for i in other.container:
            self.add(i)
        return self


    def  remove(self):
        return self.container.pop(0)


    def peek(self):
        return self.container[0]


    def __getitem__(self,index):
        return self.container[index]


    def __iter__(self):
        return Iterator(self.container)

    def concat(self,value):
        for i in value:
            self.add(i)

    def __bool__(self):
        return len(self.container)>0

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


    def __deepcopy__(self,memo):
        return Queue(copy.deepcopy(self.container,memo))

    def __lt__(self,other):
        return self.container.__lt__(other.container)

    def __eq__(self, other):
        return self.container.__eq__(other.container)

但是当我尝试使用上述类进行比较时,我得到了以下结果:
>>> from queue import Queue
>>> q = Queue([1,2,3])
>>> q
>>> print q
<Queue: [1, 2, 3]>
>>> q1 = None
>>> q==q1
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "queue.py", line 65, in __eq__
    return self.container.__eq__(other.container)
AttributeError: 'NoneType' object has no attribute 'container'
>>> 
3个回答

4

您的问题在于您如何实现__eq__

看看这段代码:

q = Queue([1,2,3])
q1 = None
q==q1

让我们把它重写为等价的形式:

q = Queue([1,2,3])
q == None

现在,在Queue.__eq__中,我们有:
def __eq__(self, other):
    return self.container.__eq__(other.container)

但是otherNone,这意味着返回语句正在调用:
self.container.__eq__(None.container)

正如您的错误正确指出的那样:

'NoneType' object has no attribute 'container'

因为它没有!None没有容器属性。

所以,处理它的方式取决于您希望如何处理它。显然,如果定义了一个Queue对象,它就不能是None,因此:

return other is not None and self.container.__eq__(other.container)

如果otherNone,它会惰性地评估,并在评估and之后的表达式部分之前返回False。否则,它将执行评估。但是,如果other不是类型为Queue(或更正确地说,另一个对象没有container属性),您将遇到其他问题,例如:

q = Queue([1,2,3])
q == 1
>>> AttributeError: 'int' object has no attribute 'container'

那么根据你的逻辑,如果一个Queue不能与其他类型“相等”(这是只有你能说的事情),您可以像这样检查正确的类型:

return other is not None and type(self) == type(other) and self.container.__eq__(other.container)

但是,None 是一个 NoneType 类型,因此它永远不可能与一个 Queue 相同的类型。所以我们可以再次简化为:

return type(self) == type(other) and self.container.__eq__(other.container)

编辑:根据mglisons的评论:

可以使用常规的等式语句使其更具Python风格。

return type(self) == type(other) and self.container == other.container

他们也提出了一个关于在检查相等性时使用type的好点子。如果您确信Queue永远不会被子类化(这是很难陈述的)。您可以使用异常处理来有效捕获AttributeError,像这样:
def __eq__(self, other):
    try:
        return self.container == other.container
    except AttributeError:
        return False    # There is no 'container' attribute, so can't be equal
    except:
        raise           # Another error occured, better pay it forward

上述方法可能被认为有点过度设计,但从安全性和可重用性的角度来看,这可能是更好的方法之一。

或者一个更好、更简短的方法(我一开始应该想到的)是使用hasattr

return hasattr(other, 'container') and self.container == other.container

为什么要使用 self.container.__eq__(...),而不是直接使用 self.container == ...?此外,我不确定是否推荐使用 type 的相等性。子类等情况怎么办? - mgilson
1
@LegoStormtroopr -- 我想你可以尝试使用hasattr来查看other是否具有container属性,并依赖于鸭子类型。 - mgilson
@mgilson:instanceof 能否代替 hasattr 呢?我可能有一个名为 Stack 的类,其中包含 container 属性,对吗? - brain storm
1
@user1988876 -- 你的意思是 isinstance 吗?当然,你可以尝试一下。例如:return isinstance(other, Queue) and self.container == other.container - mgilson
1
很大程度上取决于您作为程序员的想法。如果一个“队列”只能等于其他“队列”,而不能等于它们的子类,则“type(self) == type(other)”是有效的。但这取决于您。这里有几种方法,尽管我在上面的评论中链接的问题建议不要使用“isinstance”和类似的方法。 - user764357
显示剩余4条评论

3
告诉Python你不知道如何与其他类型进行比较:
def __eq__(self, other):
    if not isinstance(other, Queue):
        return NotImplemented
    return self.container.__eq__(other.container)

你可以考虑检查hasattr(other,'container')而不是isinstance,或者捕获AttributeError。但重要的是,不像其他答案建议的那样,如果other不是队列,你不应该返回False。如果你返回NotImplemented,Python会给other一个机会来检查相等性;如果你返回False,则不会。区分三个可能的回答,即“这些对象是否相等”:是、否、我不知道。
__lt__中,你需要做类似的事情,差异更加明显:如果你从__lt____eq__中都返回False,那么由total_ordering插入的__gt__将返回True - 即使你无法进行比较。如果你从它们两个中都返回NotImplemented,那么也将是NotImplemented

我不知道返回“NotImplemented”的用法。我以为这可能是一个错误,你的意思是抛出异常... 没有错:https://dev59.com/bXNA5IYBdhLWcg3wpvpg - steveha

1
你可以做类似以下的事情:
    def __eq__(self,other):
        if other is None: return False
        return self.container.__eq__(other.container)

你可能也想做类似这样的事情。
if not isinstance(other,Queue): return False

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接