Search code examples
pythonpython-3.xsubclass

Is there an elegant way to subclass a Python list which keeps its subclass when adding and slicing, etc


I want to extend the functionality of a standard list, subclassing seems like the obvious way. Actually, I'm using this to store a mathematical expression in Reverse Polish Notation (RPN) for a genetic programming project.

class RPN(list):
    def evaluate(self, vars):
        # evaluate the RPN expression
        pass

    def to_infix(self):
        # convert to infix form
        pass

    @staticmethod
    def from_infix(expr):
        # create new RPN object from infix expression sting
        pass

This works fine, I can create an RPN object like RPN([5, 6, operator.add, 7, 8, operator.add, operator.mult]), and it works as intended.

My problem is that I want do list-like operations to these RPN objects, such as append, add together, slice, etc. but when I do this they revert back to plain old lists (apart from append - that's ok).

So, for example:

rpn1 = RPN([5, 6, operator.add])
rpn2 = RPN([7, 8, operator.add])
rpn3 = rpn1 + rpn2
# type(rpn3) == list
rpn4 = rpn1[0:1]
# type(rpn4) == list

I can understand why, because a new object gets created, and it creates it as a list. I suppose I can change this behaviour, but then I would have to provide new implementations for __add__, slicing methods, etc. And all of a sudden, I'm writing a lot more code than I intended. Alternatively, I can remake everything as an RPN object each time, e.g. rpn3 = RPN(rpn1 + rpn2), but this also seems clunky.

Is there a better way?

Note, this question was marked as a duplicate of How to use list comprehension in list derived class. The tagged post was asking about how to use list comprehensions inside a derived list class (and in an odd way, by reassigning self variable inside __init__ function), I'm asking about something completely different. It just so happens, that both these are solved by using collections.UserList, but the questions themselves are not duplicates IMHO.


Solution

  • I'm posting this, but @jasonharper provided the answer. The solution to my problem was to subclass collections.UserList rather than list. All of its methods generate objects of the same class. Works perfectly.