簡體   English   中英

如何在 Python 中為列表類實現 undo() 方法

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

我對 python 有點新,我有一個任務來創建一個帶有 undo() 方法的類“UndoList”(類型列表)。 此方法應撤消典型的列表操作,如追加、插入、刪除...

>>> 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() 方法應該只撤銷一個操作(如你在示例中看到的)。 我的老師給了我提示,在每次操作之前將列表的值保存在實例中。

這是我的課:

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]

所以現在我的問題是:如何在我的類中創建一個實例以在我執行任何操作之前保存我的實際列表? 是否可以在我的撤銷方法中重新運行這個實例?

謝謝!

下面是一些可以幫助您入門的代碼。 但實際上,最好避免對 Python 的標准類型進行子類化,因為要正確執行此操作,您通常需要覆蓋每個方法,這可能相當乏味且容易出錯。

請注意, append方法稱為append ,而不是__append__ :) 並且就地改變列表的方法返回None ,而不是列表。

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。

正如評論中提到的 9000,您可能不需要在這里進行deepcopy 它通過遞歸復制每個列表項(不可變項除外)來消耗額外的 RAM,而且速度很慢。 使用deepcopy確實使UndoList健壯。 OTOH,這也意味着從.old恢復的項目是原始項目的副本,在某些情況下這是不可取的 - 如果其他對象引用這些項目,則備份過程會中斷該連接。

如果您想對此進行試驗,只需將備份列表的代碼更改為

self.old = self[:]

undo方法變為

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

這樣做的明智方法是使用Abstract Base Classes而不是 sub-classing list來構建一個新類。

這很簡單。 但是很乏味:你向列表對象添加了一個history屬性。 這是先前變量狀態的堆棧。 每個更改操作都需要在更改之前將其當前狀態推送到對象的history undo操作只是彈出最近的一個。

您已經注意到您必須重新定義所有更改操作(例如類中的__append__ )。

讓我們通過例子來理解:最初給定一個空數組,我們必須實現四個函數。

我們有以下類型的查詢:

  1. Add(value) :將值添加到數組中。

  2. Remove(value) :從數組中刪除值。

  3. 撤消:撤消對陣列執行的最后一個操作。

  4. 重做:恢復最近在陣列上執行的 UNDO 操作。

示例: 輸入:List=[] 輸出:add(1) print(List) List=[1]

          add(2)  print(List)  List=[1,2]
          add(3)  print(List) List=[1,2,3] 
          add(4)  print(List)  List=[1,2,3,4]
          undo()  print(List) List=[1,2,3]
          undo()  print(List)  List=[1,2]
          redo()   print(List)  List=[1,2,3]
          redo()  print(List) List=[1,2,3,4]
          remove(3) print(List)  List=[1,2,4]
          remove(1)  print(List) List=[2,4]
          remove(2)  print(List)  List=[4]
          undo() print(List)  List=[2,4]
          undo() print(List)  List=[1,2,4]
          undo() print(List)  List=[1,2,3,4]
          redo()  print(List)  List=[1,2,4]

方法:這個問題可以通過使用 2 個列表來解決,首先,我們將創建一個 undo 列表,該列表將跟蹤最后執行的操作,它將是一個元組列表,格式為(操作,索引,值) 如果 operation='a' 表示執行的最后一個動作是將元素添加到列表中,並且如果 operation='r' 表示執行的最后一個動作是從列表中刪除元素。

第二個列表將是重做列表,它將跟蹤執行的撤消操作,以便它可以恢復最近的撤消操作。

 # Python Program to implement the above approach. # this is the main list which will be printed after doing any of the above operations main_list = [] # this is the list for tracking the operations being performed undo_list = [] # this is the redo list which will keep track of the undo operations done. redo_list = [] \\ def add(value): """ this is the function to add the value to the list """ # index at will we will add the value idx=len(main_list) # value will be added to the main_list main_list.append(value) # we will update undo_list, by appending the operation as 'r', as we are adding value to the list, so its undo operation will do the reverse of it, so we will append operation as 'r'. undo_list.append(('r',idx,value)) print(main_list) def remove(value): """ this is the function to remove the value from the list """ # length of the main_list length=len(main_list) # if the length of the main_list is 0 if(length==0): return # if the value is not present in the main_list if value not in main_list: return # index of the value that we have to remove idx = main_list.index(value) # removing value from the main_list main_list.remove(value) # we will update undo_list, by appending the operation as 'a', as we are removing value from the list , so its undo operation will do the reverse of it , so we will append operation as 'a'. undo_list.append(('a', idx, value)) print(main_list) def undo(): """ this is the function to undo the value """ #length of the undo_list length = len(undo_list) # if the length of the undo_list is 0 ,means there is nothing to do undo operation if(length==0): return # selecting the latest undo operation that we have to perform cur_tuple=undo_list.pop(); # selecting the type of the operation that we have to perform cur_tuple_operation=cur_tuple[0] # selecting the index at which we will perform latest undo operation. cur_tuple_index=cur_tuple[1] # selecting the value on which we will perform the latest undo operation cur_tuple_value=cur_tuple[2] # if the operation we have to do undo is 'a' if(cur_tuple_operation=='a'): # adding value to main_list main_list.insert(cur_tuple_index,cur_tuple_value) # also we will update redo_list by appending the operaion as 'r' as the undo current operation is 'a' , so redo operation will restore the most recent undo operation beging performed. redo_list.append(('r',cur_tuple_index,cur_tuple_value)) # if the operation we have to do undo is 'r' elif(cur_tuple_operation=='r') : # removing the value from the main_list main_list.pop(cur_tuple_index) # also we will update redo_list,by appending the operation as 'a', as the undo current operation is 'r' , so redo operation will restore the most recent undo operation beging performed. redo_list.append(('a',cur_tuple_index,cur_tuple_value)) print(main_list) def redo(): """ this is the function to redo the value """ #length of the redo_list length=len(redo_list) # if the length of the redo list is 0 if(length==0): return # selecting the latest redo operation that we have to perform. cur_tuple=redo_list.pop(); # selecting the type of the operation that we have to perform cur_tuple_operation=cur_tuple[0] # selecting the index at which we will perform latest redo operation. cur_tuple_index=cur_tuple[1] # selecting the value on which we will perform the latest redo operation. cur_tuple_value=cur_tuple[2] # if the operation we have to do redo is 'a' if(cur_tuple_operation=='a'): # adding value to main_list main_list.insert(cur_tuple_index,cur_tuple_value) # if the operation we have to do redo is 'r' elif(cur_tuple_operation=='r'): # removing the value from the main_list main_list.pop(cur_tuple_index) print(main_list) add(1) add(2) add(3) remove(2) add(4) add(5) undo() undo() undo() undo() undo() undo() redo() redo() redo() redo() redo() redo()

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM