Assertion fails for collections.deque

70 views Asked by At

I can't see why I get an assertion error for the following code. It looks to me like the output from the print statement should be equivalent to the return value of queue_challenge() but not so. Can someone please explain why, and what the correct assertion should be?

from collections import deque


class Queue:
    def __init__(self):
        self.items = deque()

    def is_empty(self):
        return not self.items
        # return len(self.items) == 0

    def enqueue(self, item):
        self.items.append(item)

    def dequeue(self):
        return self.items.popleft()

    def size(self):
        return len(self.items)

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

    def __str__(self):
        return str(self.items)


def queue_challenge():
    q = Queue()
    q.enqueue("Learning")
    q.enqueue("is")
    q.dequeue()
    q.enqueue("great")
    q.enqueue("fun")
    q.dequeue()

    return q


print(queue_challenge())  # deque(['great', 'fun'])

assert queue_challenge() == deque(['great', 'fun'])
2

There are 2 answers

0
aiootp On

The __eq__ can be overwritten as follows to test for the kind of equality you may be looking for:

class Queue:
    # omitting the other methods of the class for succinctness
    ...

    def __eq__(self, other) -> bool:
        items = (
            other.items
            if issubclass(self.__class__, other.__class__)
            else other
        )
        return self.items == items

print(queue_challenge() == deque(['great', 'fun']))
True

0
snakecharmerb On

A Queue can't be compared with a deque because a Queue is not a deque, it wraps a deque (as slothrop correctly observed, it's a "Has-A" relationship).

You can override __eq__ to use the internal deque if it finds that it is being compared with an actual deque, but this isn't ideal for a few reasons:

  • it leaks Queue's implementation details to the outside world. If you were to decide to swap Queue's deque for some other type it would be problematic if there was a lot of client code comparing deques with Queues.

  • Users of your code may be surprised to find that Queues can compare equal to things that aren't Queues.

  • The comparison is too limited. If a Queue can be compared to a deque, why can't it be compared to other ordered containers like lists or tuples?

For these reasons, personally I would give Queue some kind of matches_collection method that lets you compare the Queue's contents with other containers without placing too many restrictions on either side of the comparison, something like (untested, not production-ready etc):

from collections.abc import Sequence
class Queue:
    ....

    def matches_collection(self, collection):
        # TODO strings :-)
        if not isinstance(collection, Sequence):
            raise TypeError(f"Can't compare Queue with {type(collection)}")
        return list(self.deque) == list(collection)