Search code examples
pythonmultiple-inheritancepriority-queuedeque

Inherit from both 'heapq' and 'deque' in python?


I'am trying to implement a 'heapq' or a 'deque' dynamically (according to user's input)

class MyClass():

    def __init__(self,  choose = True ):
    self.Q = []
    self.add = self.genAdd(choose)
    self.get = self.genGet(choose)

    def genAdd(self, ch):
        if(ch == True):
            def f(Q, elem):
                return Q.append
        else:
            def f(Q):
                return heappush
        return f

and same for 'genGet'

the execution is correct on one side (x)or the other (but not both at the same time). I get things like

TypeError: f() takes exactly 1 argument (2 given)

tried multiple inhreitance but got

TypeError: Error when calling the metaclass bases
metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases

the problem is that heapq is called with

heappush(Q, elem)

and queue with

Q.append(elem)

I hope the point is clear. I think there should be a way to fix that (maybe using lambda)

Thanks


Solution

  • Inheritance isn't going to help here.

    First, heapq isn't even a class, so you can't inherit from it. You can write a class that wraps up its functionality (or find one on the ActiveState recipes or in a PyPI package), but you have to have a class to inherit.

    But, more importantly, the whole point of inheritance is to give you an "is-a" relationship. This thing you're building isn't-a deque, or a heapq-wrapping object, it's a thing with an interface that you've defined (add and get) that happens to use either a deque or a list with heapq for implementation.

    So, just do that explicitly. You're trying to define a function that either calls append on a deque, or calls heapq.heappush on a list. You're not trying to write a curried function that returns a function that does the thing, just a function that does the thing.

    def genAdd(self, ch):
        # As a side note, you don't need to compare == True, nor
        # do you need to wrap if conditions in parens.
        if ch:
            def f(elem):
                self.Q.append(elem)
        else:
            def f(elem):
                heappush(self.Q, elem)
        return f
    

    There are a few other problems here. First, you definitely need to set self.Q = deque() instead of self.Q = [] if you wanted a deque. And you probably want to wrap these functions up as a types.MethodType instead of using self as a closure variable (this will work, it's just less readable, because it may not be clear to many people why it works). And so on. But this is the fundamental problem.


    For example:

    from collections import deque
    from heapq import heappush
    
    class MyClass(object):
        def __init__(self, choose=True):
            self.Q = deque() if choose else []
            self.add = self.genAdd(choose)
    
        def genAdd(self, ch):
            if ch:
                def f(elem):
                    self.Q.append(elem)
            else:
                def f(elem):
                    heappush(self.Q, elem)
            return f
    
    d = MyClass(True)
    d.add(3)
    d.add(2)
    print(d.Q)
    
    h = MyClass(False)
    h.add(3)
    h.add(2)
    print(h.Q)
    

    This will print:

    deque([3, 2])
    [2, 3]
    

    That being said, there's probably a much better design: Create a class that wraps a deque in your interface. Create another class that wraps a list with heapq in your interface. Create a factory function that returns one or the other:

    class _MyClassDeque(object):
        def __init__(self):
            self.Q = deque()
        def add(self, elem):
            self.Q.append(elem)
    
    class _MyClassHeap(object):
        def __init__(self):
            self.Q = []
        def add(self, elem):
            heappush(self.Q, elem)
    
    def MyClass(choose=True):
        return _MyClassDeque() if choose else _MyClassHeap()
    

    Now you get the same results, but the code is a lot easier to understand (and slightly more efficient, if you care…).