在Python中以非常大的状态撤消。可能吗?

时间:2017-12-07 04:24:44

标签: python undo

这看似简单,但我无法找到一个好的解决方案。

旧的'传递参考' /'传递价值' /'通过对象引用'问题。我理解发生了什么,但我找不到好的工作。

我知道小问题的解决方案,但我的状态非常大,保存/重新计算的成本非常高。鉴于这些限制,我无法找到解决方案。

这是一个简单的伪代码来说明我想做什么(如果Python允许我通过引用传递):

class A:
    def __init__(self,x):
        self.g=x
        self.changes=[]
    def change_something(self,what,new): # I want to pass 'what' by reference
        old=what # and then de-reference it here to read the value
        self.changes.append([what,old]) # store a reference
        what=new # dereference and change the value
    def undo_changes():
        for c in self.changes:
            c[0]=c[1] # dereference and restore old value

编辑:添加更多伪代码以显示我希望如何使用上述

test=A(1) # initialise test.g as 1

print(test.g)
out: 1

test.change_something(test.g,2)
# if my imaginary code above functioned as described in its comments,
# this would change test.g to 2 and store the old value in the list of changes

print(test.g)
out: 2

test.undo_changes()

print(test.g)
out: 1

显然上面的代码在python中不起作用,因为它是通过对象引用'来传递的。此外,我希望能够撤消单个更改,而不仅仅是上面的代码中的所有更改。

事情是......我似乎无法找到一个好的工作。有这样的解决方案:

Do/Undo using command pattern in Python

making undo in python

涉及存储一堆命令。 '撤消'然后涉及删除最后一个命令,然后通过获取初始状态并重新应用除最后一个命令之外的所有内容来重新构建最终状态。我的状态太大,不可行,问题是:

  • 国家非常大。完全保存它是非常昂贵的。
  • '待办事项'操作成本很高(从保存的状态重新计算不可行)。
  • 操作也是非确定性的,依赖于随机输入。
  • 撤消操作非常频繁

我有一个想法,即确保将所有内容存储在列表中,并编写我的代码,以便将所有内容存储,读取和写入这些列表。然后在上面的代码中,每次我想读/写变量时,我都可以传递列表名称和列表索引。

基本上这相当于在Python中构建我自己的内存架构和C风格的指针系统! 这有效,但似乎有点......太荒谬了?当然有更好的方法吗?

2 个答案:

答案 0 :(得分:1)

请检查它是否有帮助......

class A:
def __init__(self,x):
    self.g=x
    self.changes={}
    self.changes[str(x)] = {'init':x, 'old':x, 'new':x}   #or make a key by your choice(immutable)
def change_something(self,what,new): # I want to pass 'what' by reference
    self.changes[what]['new'] = new #add changed value to your dict
    what=new # dereference and change the value
def undo_changes():
    what = self.changes[what]['old'] #retrieve/changed to the old value
    self.changes[what]['new'] = self.changes[what]['old'] #change latest new val to old val as you reverted your changes

对于每个更改,您都可以更新change_dictionary。你需要弄清楚的是,#34;如何创建自我更改字典中的键的条目",我只是将其作为str(x),只需检查类型(什么)以及如何制作它是你案件的关键。

答案 1 :(得分:1)

好的,所以我想出了一个答案......但它太丑了!我怀疑这是最好的解决方案。它使用exec(),我被告知这是不好的做法,如果可能的话应该避免。编辑:见下文!

使用exec()的旧代码:

class A:
    def __init__(self,x):
        self.old=0
        self.g=x
        self.h=x*10
        self.changes=[]
    def change_something(self,what,new):
        whatstr='self.'+what
        exec('self.old='+whatstr)
        self.changes.append([what,self.old]) 
        exec(whatstr+'=new') 
    def undo_changes(self):
        for c in self.changes:
            exec('self.'+c[0]+'=c[1]')
    def undo_last_change(self):
        c = self.changes[-1]
        exec('self.'+c[0]+'=c[1]')
        self.changes.pop()

感谢barny,这是一个使用getattr和setattr的更好的版本:

class A:
    def __init__(self,x):
        self.g=x
        self.h=x*10
        self.changes=[]
    def change_something(self,what,new):
        self.changes.append([what,getattr(self,what)])
        setattr(self,what,new) 
    def undo_changes(self):
        for c in self.changes:
            setattr(self,c[0],c[1])
    def undo_last_change(self):
        c = self.changes[-1]
        setattr(self,c[0],c[1])
        self.changes.pop()

要演示,输入:

print("demonstrate changing one value")
b=A(1)
print('g=',b.g)
b.change_something('g',2)
print('g=',b.g)
b.undo_changes()
print('g=',b.g)

print("\ndemonstrate changing two values and undoing both")
b.change_something('h',3)
b.change_something('g',4)
print('g=', b.g, 'h=',b.h)
b.undo_changes()
print('g=', b.g, 'h=',b.h)

print("\ndemonstrate changing two values and undoing one")
b.change_something('h',30)
b.change_something('g',40)
print('g=', b.g, 'h=',b.h)
b.undo_last_change()
print('g=', b.g, 'h=',b.h)

返回:

demonstrate changing one value
g= 1
g= 2
g= 1

demonstrate changing two values and undoing both
g= 4 h= 3
g= 1 h= 10

demonstrate changing two values and undoing one
g= 40 h= 30
g= 1 h= 30

编辑2:实际上......经过进一步的测试,我的exec()初始版本比第二版有一些优势。如果类包含第二个类,列表或其他类,则exec()版本在类中更新类中的列表时没有问题,但第二个版本将失败。