2017-05-31 49 views
1

林有點新的與python和我有一個任務創建一個類「UndoList」(類型列表)與撤消() - 方法。此方法應該取消像典型的列表操作,追加,插入,刪除...如何實現一個撤消() - 在Python中的列表類的方法

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

此撤銷() - 方法只能撤銷一次操作(如u可以看到的例子)。我的老師給我提示,在每次操作之前保存實例中的列表值。

這是我的課:

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] 

所以現在我的問題:我怎麼能在我的課,以節省我的實際列表創建一個實例。我做任何操作之前?是否有可能在我的撤消方法中重新運行此實例?

謝謝!

+0

那麼,你想讓我們爲你解決你的課程練習嗎? – yorodm

+1

你應該實現某種「歷史」屬性。 – MSeifert

+0

你的意思是說你只需要支持一個級別的撤消? –

回答

3

以下是一些可以幫助您入門的代碼。實際上,最好避免對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而不是子分類list來建立新班級。

+0

這無疑是一個可行的解決方案,但它是一個非常耗費內存的解決方案。 OP有可能沒有要求屏蔽變化列表中的可變數據。 – 9000

+0

這很好用!非常感謝,哥們。現在我將嘗試使用抽象基類來構建它。非常感謝! –

+1

@ 9000也許'deepcopy'是過度殺傷性的,我同意這對'append'或'extend'來說不是必須的,但是'remove'是必須的,因爲被刪除的項目可能會在其他代碼被「撤消」之前被突變'調用,然後'撤消'不會將列表恢復到之前的狀態。 OTOH,有其他代碼突變的列表項在使用列表時總是有可能... –

0

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

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