如何在Python中为列表类实现undo()方法 [英] How to implement an undo()-Method for a List-Class in Python

查看:287
本文介绍了如何在Python中为列表类实现undo()方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是python的新手,我有一个任务,要使用undo()方法创建一个类"UndoList"(类型列表).此方法应撤消典型的列表操作,如追加,插入,删除...

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]
...

此undo()方法仅应撤消一项操作(如您在示例中所见).我的老师给了我提示,在每次操作之前将列表的值保存在实例中.

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.

这是我的课程:

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?

谢谢!

推荐答案

下面的一些代码可以帮助您入门.确实,尽管如此,最好避免对Python的标准类型进行子类化,因为要正确地做到这一点,通常需要覆盖每个方法,这可能非常乏味且容易出错.

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.

请注意,append方法被称为append,而不是__append__. :)并且原位更改列表的方法返回None,而不是列表.

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)

输出

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

我们使用def __init__(self, *args),以便我们可以在没有参数的情况下调用UndoList()以获得空的UndoList.

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

正如9000在评论中提到的那样,您这里可能不需要deepcopy.通过递归复制每个列表项(不可变项除外)会消耗额外的RAM,而且速度很慢.使用deepcopy确实使UndoList健壮. OTOH,这也意味着从.old恢复的项目是原始项目的副本,在某些情况下,这是不可取的-如果其他对象引用了这些项目,则备份过程会中断该连接.

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[:]

undo方法变为

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


执行此操作的理智方法是使用抽象基类,而不是对list进行子分类.


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

这篇关于如何在Python中为列表类实现undo()方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆