How to implement an undo()-Method for a List-Class in Python

你说的曾经没有我的故事 提交于 2019-12-08 07:22:40

问题


Im kinda new with python and I got a task to create a class "UndoList" (type list) with an undo()-method. This method should undo typical list-operations like, append, insert, remove...

>>> ul = UndoList([1,2,3])
>>> ul.append(4), print(ul), undo(ul), print(ul)
[1,2,3,4]
[1,2,3]
>>> ul.remove(3), print(ul), undo(ul), print(ul)
[1,2]
[1,2,3]
...

This undo()-method should only undo one operation (as u can see in the example). My teacher gave me the hint, to save the value of the list in the instance before every operation.

This is my class:

class UndoList(list):

   def __init__(self, lis):
       list.__init__(self, lis)
       self.lis = []

   def __append__(self, lis):
       list.__add__(self, lis)
       return lis

   def undo(self):
       return self

a1 = UndoList([1,2,3])
print(a1), a1.append(4), print(a1)   #[1,2,3] [1,2,3,4]
a1.undo(), print(a1)                 #[1,2,3,4]

So now my question: how can i create an instance in my class to save my actual list before I do any operation? And is it possible to just retrun this instance in my undo-method?

Thanks!


回答1:


Here's some code that will get you started. Really though, it's best to avoid sub-classing Python's standard types because to do it properly you generally need to override every method, which can be rather tedious and error-prone.

Note that the append method is called append, not__append__. :) And that the methods which mutate a list in-place return None, not the list.

from copy import deepcopy

class UndoList(list):
    def __init__(self, *args):
        super().__init__(*args)
        self.old = []

    def append(self, item):
        self.old = deepcopy(self[:])
        super().append(item)

    def extend(self, items):
        self.old = deepcopy(self[:])
        super().extend(items)

    def undo(self):
        temp = deepcopy(self[:])
        self[:] = self.old
        self.old = temp


a = UndoList([1, 2, 3])
print(a)

a.append(4)
print(a)
a.undo()
print(a)
a.undo()
print(a)

a.extend([5, 6])
print(a)
a.undo()
print(a)

output

[1, 2, 3]
[1, 2, 3, 4]
[1, 2, 3]
[1, 2, 3, 4]
[1, 2, 3, 4, 5, 6]
[1, 2, 3, 4]

We use def __init__(self, *args) so that we can call UndoList() with no args to get an empty UndoList.

As 9000 mentions in the comments, you probably don't need deepcopy here. It consumes extra RAM by recursively copying every list item (except for immutable items), and it's slow. Using deepcopy does make UndoList robust. OTOH, it also means that items restored from .old are copies of the original items, and in some cases that is undesirable - if other objects refer to those items then the back-up process breaks that connection.

If you want to experiment with this, simply change the code that backs up the list to

self.old = self[:]

and the undo method becomes

def undo(self):
    self[:], self.old = self.old, self[:]

The sane way to do this is to build a new class using Abstract Base Classes rather than sub-classing list.




回答2:


It's simple. but tedious: you add a history attribute to the list object. This is a stack of previous variable states. Every change operation needs to push its current state onto the object's history before the change. The undo operation simply pops the most recent one.

You've already noted that you'll have to redefine all of the change operations (such as the __append__ in your class).



来源:https://stackoverflow.com/questions/44291944/how-to-implement-an-undo-method-for-a-list-class-in-python

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!