如何取消最后一个叫做的方法?

时间:2018-01-09 17:34:58

标签: python python-3.x

class Minobot:
    def __init__(self):
        self.x,self.y = 0, 0
        self.angle = 90

    def forward(self, d):
        angle = radians(90-self.angle)
        nx, ny = self.x + d * cos(angle), self.y + d * sin(angle)
        self.x, self.y = nx, ny

    def right(self):
        self.angle += 90

    def left(self):
        self.angle -= 90

    def coordinates(self):
        return round(self.x, 1), round(self.y, 1)

    def manhattan_distance(self):
        return int(abs(round(self.x, 1))+abs(round(self.y, 1)))

    def cancel(self):
        ????

现在我需要在这个类中添加另一个方法来取消最后一个被调用的方法。 例如:a.forward(2)=> a.right()=> a.cancel()这将在使用a.right()之前设置Minobot。

6 个答案:

答案 0 :(得分:4)

除非您存储,否则无法取消上一个操作。

如果存储了上一个操作,则可以将其反转。由于您在采取行动后知道反向行动,因此您可以直接存储反向行动。

让您的Minibot实例具有.reverse_action属性,该属性是要调用的方法和要传递的参数的元组。

所以

def left(self):
   # Note how methods can just be stored in variables.
   self.reverse_action = (self.right, ())
   ...

def forward(self, distance):
   # Forward is its own reverse with a negative distance.
   self.reverse_action = (self.forward, (-distance,))

def revert_last(self):
   if self.reverse_action:
      (method, args) = self.reverse_action
      method(*args)  # Call the stored method, passing stored args.
      self.reverse_action = None  # Used it up.

这有一个明显的缺点,即只能恢复最后一个动作。如果为每个操作存储了反向操作的列表,只要列表中存在任何存储的反向操作,您就可以.pop()从中恢复操作。

如果您采取了很多操作并且受内存限制,则只能存储最后几个操作。 (谷歌的条款:"撤消缓冲区","循环缓冲区","事件采购"。)

另一种方法是存储先前的状态,即坐标,标题等。撤消最后一个动作然后只需切换到先前的状态:

def save_state(self):
  self.previous_state = (self.x, self.y, self.angle)
  # Or: self.previous_states_list.append(...)

def restore_previous_state(self):
  (self.x, self.y, self.angle) = self.previous_state
  # Or: ... = self.previous_states_list.pop()

def left(self): 
  self.save_state()
  ...

这种方法没有舍入错误等。但是,它需要更多的内存,特别是当你的状态变大时,以及当你想保存以前状态的整个历史时。

答案 1 :(得分:3)

您可以保存所有属性,而不是像其他答案所建议的那样保存撤消操作,cancel可以恢复它们。

class Minobot:
    def __init__(self):
        self.x,self.y, self.oldx, self.oldy = 0, 0
        self.angle, self.oldangle = 90

    def forward(self, d):
        self.oldx, self.oldy = self.x, self.y
        angle = radians(90-self.angle)
        nx, ny = self.x + d * cos(angle), self.y + d * sin(angle)
        self.x, self.y = nx, ny

    def right(self):
        self.oldangle = self.angle
        self.angle += 90

    def left(self):
        self.oldangle = self.angle
        self.angle -= 90

    def coordinates(self):
        return round(self.x, 1), round(self.y, 1)

    def manhattan_distance(self):
        return int(abs(round(self.x, 1))+abs(round(self.y, 1)))

    def cancel(self):
        self.angle, self.x, self.y = self.oldangle, self.oldx, self.oldy

答案 2 :(得分:1)

您可以保存最后一个操作并反向运行: (感谢那个让我用lambdas更新的人)

class Minobot:
def __init__(self):
    self.x,self.y = 0, 0
    self.angle = 90
def forward(self, d):
    angle = radians(90-self.angle)
    nx, ny = self.x + d * cos(angle), self.y + d * sin(angle)
    self.x, self.y = nx, ny
    self.undo = lambda:self.forward(-d)

def right(self):
    self.angle += 90
    self.undo = lambda:self.left()

def left(self):
    self.angle -= 90
    self.undo = lambda:self.right()

def coordinates(self):
    return round(self.x, 1), round(self.y, 1)

def manhattan_distance(self):
    return int(abs(round(self.x, 1))+abs(round(self.y, 1)))

def cancel(self):
    self.undo()

注意:如果您在没有取消的情况下运行取消将导致问题

答案 3 :(得分:1)

您可以尝试保留所有先前状态的记录,但内存使用量可能会增长到比您想要的更大。在下面显示的解决方案中,pickle协议用于根据需要获取和设置对象的状态。请注意,如果您挑选以下类的实例,则实例将不会保留其历史记录。代码可能需要根据您的特定要求进行调整。

#! /usr/bin/env python3
import math


class MinnowBot:
    __slots__ = '__x', '__y', '__angle', '__history'

    def __init__(self):
        self.__x = 0
        self.__y = 0
        self.__angle = 90

    def forward(self, distance):
        self.history.append(self.__getstate__())
        angle = math.radians(90 - self.__angle)
        self.__x += distance * math.cos(angle)
        self.__y += distance * math.sin(angle)

    def right(self):
        self.history.append(self.__getstate__())
        self.__angle += 90

    def left(self):
        self.history.append(self.__getstate__())
        self.__angle -= 90

    @property
    def coordinates(self):
        return round(self.__x, 1), round(self.__y, 1)

    @property
    def manhattan_distance(self):
        return round(abs(self.__x) + abs(self.__y))

    def cancel(self):
        self.__setstate__(self.history.pop())

    def __getstate__(self):
        return self.__x, self.__y, self.__angle

    def __setstate__(self, state):
        self.__x, self.__y, self.__angle = state

    @property
    def history(self):
        try:
            history = self.__history
        except AttributeError:
            # noinspection PyAttributeOutsideInit
            history = self.__history = []
        return history

答案 4 :(得分:0)

您可以通过简短解释添加州属性和:

class Minobot:
  def __init__(self):
      self.x,self.y = 0, 0
      self.angle = 90
      self.state = self.__init__
      self.d = 0

  def forward(self, d=None):
      if d is None:
          d = self.d
      angle = radians(90-self.angle)
      nx, ny = self.x + d * cos(angle), self.y + d * sin(angle)
      self.x, self.y = nx, ny
      self.d = d
      self.state = self.forward

  def right(self):
      self.angle += 90
      self.state = self.left

  def left(self):
      self.angle -= 90
      self.state = self.right

  def coordinates(self):
      return round(self.x, 1), round(self.y, 1)

  def manhattan_distance(self):
      return int(abs(round(self.x, 1))+abs(round(self.y, 1)))

  def cancel(self):
      if self.state is None:
          pass
      else:
          state = self.state
          self.state = None
          return state()

答案 5 :(得分:0)

如果不考虑内存,可以通过保存/恢复整个对象来存储/恢复状态。

我也在考虑控制其类外的目标对象不是问题。我假设该对象不是取消最后一个动作本身,但其他一些代码控制它。否则,您将必须使用Stack类管理您的类关系。

在下面的示例中,我使用deepcopy,但也可以使用pickle.dumps。 我选择使用类似堆栈的结构,它允许保存您想要的任何数量的对象/状态。但是用旋转缓冲区替换将只允许存储最后n个对象/状态。

我在其他一些答案中看到的优势并不是担心调用哪些方法或属性发生了变化。它也很简单。保留状态和框架/环境总是让我记住dicts和stacks:

from copy import deepcopy

class ObjectStack():

    def __init__(self):
        self.objects = []

    def push(self, obj):
        self.objects.append(deepcopy(obj))

    def pop(self):
        return self.objects.pop() if self.objects else None

#just an example class, not OP's original class
class char():
    def __init__(self, char):
        self.char = char

stack = ObjectStack()

c = char('C')
stack.push(c) # save object

c = char('D') # somehow object changes
print(c.char)

last = stack.pop() # restore last object/state
if last is not None:
    c = last
    print(c.char)

last = stack.pop()
if last is None:
    print('No more objects/states saved')