哪个类应该存储查找表?

时间:2011-02-02 22:32:26

标签: python oop

世界在不同位置包含代理,在任何位置只有一个代理。每个代理都知道他在哪里,但我还需要快速检查给定位置是否有代理。因此,我还保留了从地点到代理商的地图。我在确定此地图所属的位置时遇到问题:class Worldclass Agent(作为类属性)或其他地方。

在下文中,我将查找表agent_locations放在class World中。但现在代理商每次搬家时都必须致电world.update_agent_location。这很烦人;如果我稍后决定跟踪代理商的其他事情,除了他们的位置之外怎么办?我是否需要在Agent代码中将呼叫添加回世界对象?

class World:
  def __init__(self, n_agents):
    # ...
    self.agents = []
    self.agent_locations = {}
    for id in range(n_agents):
      x, y = self.find_location()
      agent = Agent(self,x,y)
      self.agents.append(agent)
      self.agent_locations[x,y] = agent
  def update_agent_location(self, agent, x, y):
    del self.agent_locations[agent.x, agent.y]
    self.agent_locations[x, y] = agent
  def update(self): # next step in the simulation
    for agent in self.agents:
      agent.update() # next step for this agent
  # ...

class Agent:
  def __init__(self, world, x, y):
    self.world = world
    self.x, self.y = x, y
  def move(self, x1, y1):
    self.world.update_agent_location(self, x1, y1)
    self.x, self.y = x1, y1
  def update():
    # find a good location that is not occupied and move there
    for x, y in self.valid_locations():
      if not self.location_is_good(x, y):
        continue
      if self.world.agent_locations[x, y]: # location occupied
        continue
      self.move(x, y)

我可以将agent_locations放在class Agent作为类属性。但这只有在我有一个World对象时才有效。如果我后来决定实例化多个World对象,那么查找表将需要特定于世界。

我相信有更好的解决方案......

编辑:我在代码中添加了几行来说明如何使用agent_locations。请注意,它仅用于Agent个对象内部,但我不知道是否会永远存在这种情况。

3 个答案:

答案 0 :(得分:2)

好的,我想我可以提供我的答案,这可能更像是一种意见,而不是一个明确的“这样做”(我没有任何正式的编程培训)。

我认为您agent_locations应该是每个World个实例的成员。

我试着主要考虑界面。在我看来,世界级应该负责管理你的世界的资源,在这种情况下是空间。由于World是空间的管理者,因此,如果空间可用(即未被占用),则代理应该询问他们的世界,而不是彼此。因此,我认为self.location_is_good更恰当的呼叫是self.world.is_location_available(x, y) [1]

这使得世界自然负责查找给定空间的可用性。此外,世界级可能还有其他变量来决定空间是否可用。如果那里有灌木丛怎么办?或者其他的东西。您可能已经在每个世界的(x, y)坐标上都有某种表格。 “被占领”可以是这些物品的财产。

此外:您的世界已经知道每个代理的状态([(agent.x, agent.y) for agent in self.agents] [2])。 agent_locations dict本质上是这些属性的索引或缓​​存,因此属于World

关于将状态发送回World ......的痛苦,您不会通过让Agent代替它来解决这个问题。但是update_agent_location(self, agent, x, y)完全是多余的,因为x == agent.x; y == agent.y(如果你反转你调用它的行)。您可以在World update_agent_state(self, agent)中使用一种方法,World可以使用它来更新其索引。您甚至可以提交一个额外的参数来描述状态更改的类型(如果您不想更新所有属性eveytime)。

class World(object):
  # ...
  def update_agent_state(self, agent, state_change=None):
    # Update properties based on what changed, or
    # drop state_change param and update everything everytime
    if state_change == Agent.LOCATION_CHANGE:
      self.agent_locations[agent.x, agent.y] = agent
    elif state_change == Agent.WHATEVER:
      pass

class Agent(object):
  LOCATION_CHANGE = 1

  def update(self):
    for x, y in self.valid_locations():
      if not self.can_move_to(x, y)
        continue

      self.move(x, y)

  def can_move_to(self, x, y):
    """Determines if x, y is a location where we can move."""
    if not self.world.is_location_available(x, y):
      return False
    if not self.has_money_to_travel_to(x, y):
      return False

    return True

  def move(self, x, y):
    """Moves to x, y and notifies world of state change."""
    self.x = x
    self.y = y

    self.world.update_agent_state(self, Agent.LOCATION_CHANGE)

类似的东西(阅读我的脚注)。

[1]当然,除非空间是自由的,否则位置的“善”取决于其他变量。例如。如果您只应移动到(x,y),如果1)该位置可用且2)代理商有1000美元来支付票证,那么您应该有一个Agent.can_move_to(x, y)反过来调用世界的方法,并检查它的钱包。

[2]我假设你的self.agents = {}是拼写错误,因为你不能在{d}上append。你的意思是一个列表([])对吧?

答案 1 :(得分:1)

有助于OOP根据拥有来讨论对象。 World 列表为Agents,列表为LocationsLocation 有一个 AgentAgent 有一个 Location和一个World

class Agent:
    def __init__(self, world):
        self.location = None
        self.world = world

    def move(self, new_location):
        if self.location is not None:
            self.location.agent = None
        new_location.agent = self
        self.location = new_location

    def update(self):
        for new_location in self.world.locations:
            if self.location_is_good(new_location):
                self.move(new_location)

    def location_is_good(self, location):
        if location.agent is not None:
            return False

class Location:
    def __init__(self, x, y):
        self.x = x
        self.y = y
        self.agent = None

进行向Location添加新属性的心理练习,例如地形,并且很容易看到这种封装的好处。同样,向Agent添加新内容(例如武器)只需要类似于move()的武器特定功能。请注意,World根本不需要参与move()。此移动严格在AgentLocation

之间处理

答案 2 :(得分:0)

抱歉,我不明白这个问题。 “”当代理人决定搬家时,他会检查以确保他不会遇到其他代理人“”“。显然,一个位置可以有0或1个代理。当然,Location类有一个代理属性。

locn = self.where_to_move()
if locn.agent is None:
    self.move(locn)
elif locn.agent is self:
    raise ConfusedAgentError()
else:
    self.execute_plan_B()