2011-02-02 11 views
5

El mundo contiene agentes en diferentes ubicaciones, con un solo agente en cualquier ubicación. Cada agente sabe dónde está, pero también necesito verificar rápidamente si hay un agente en una ubicación determinada. Por lo tanto, también mantengo un mapa de ubicaciones a agentes. Tengo un problema para decidir a dónde pertenece este mapa: class World, class Agent (como un atributo de clase) o en otro lugar.¿Qué clase debe almacenar la tabla de búsqueda?

A continuación, puse la tabla de búsqueda, agent_locations, en class World. Pero ahora los agentes tienen que llamar al world.update_agent_location cada vez que se mudan. Esto es muy molesto ¿Qué sucede si más tarde decido realizar un seguimiento de otras cosas sobre los agentes, además de sus ubicaciones? ¿Tendría que volver a agregar las llamadas al objeto mundial a través del código 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) 

Yo en cambio puede poner en agent_locationsclass Agent como un atributo de clase. Pero eso solo funciona cuando tengo un solo objeto World. Si luego decido instanciar varios objetos World, las tablas de búsqueda deberían ser específicas del mundo.

Estoy seguro de que hay una mejor solución ...

EDIT: He añadido unas líneas para el código para mostrar cómo se utiliza agent_locations. Tenga en cuenta que solo se usa desde el interior de los objetos Agent, pero no sé si eso seguirá siendo así para siempre.

+0

¿No tendría que actualizar 'Agent.agent_locations' independientemente? ¿Cuán "rápido" debe ser su verificación rápida? ¿Cuántos agentes tienes? Si no tiene tantos (relativos), podría iterar sobre los agentes de un Mundo, verificando sus ubicaciones. – vicvicvic

+0

Cuando el agente decide dónde moverse, comprueba para asegurarse de que no se encuentre con otros agentes. Si repito todos los agentes para verificar eso, terminaría buscando la ubicación de cada agente (realizando operaciones 'n_agents') cada vez que se mueva algún agente. La actualización de la tabla de búsqueda solo requiere una operación por 'movimiento '. – max

+0

El código que proporcionó no lo comprueba. 'update_agent_location' simplemente sobrescribe a quien estaba en' (x, y) 'antes ... De todos modos, ¿movería la tabla de búsqueda a la clase' Agente' realmente resolvería algo? ¿Todavía no tendrías que llamar 'Agent.update_agent_location'? – vicvicvic

Respuesta

1

Ayuda con la programación orientada a objetos para hablar de los objetos en términos de es un y tiene un. A Worldtiene una lista de Agents y una lista de Locations. A LocationtieneAgent. Un Agenttiene unLocation y un 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 

pasar por el ejercicio mental de añadir un nuevo atributo a un Location, tales como el terreno, y es fácil ver los beneficios de dicha encapsulación. Del mismo modo, agregar cosas nuevas al Agent, como un arma, solo requiere una función específica del arma similar a move(). Tenga en cuenta que el World no necesita involucrarse en el move() en absoluto. El movimiento se maneja estrictamente entre Agent y Location.

+0

¿No sería peligroso tener la tabla de búsqueda almacenada en una clase ('Ubicación') pero actualizada de otra (' Agente')? Siempre traté de evitar tocar partes internas de una clase de otra. – max

+0

Es en realidad un [paradigma bastante común] (http://docs.python.org/tutorial/classes.html#odds-and- end) en python. La convención es prefijar a los miembros de datos con un guión bajo si no desea que se utilicen de esa manera. Siempre puedes agregar los métodos 'get_agent()' y 'set_agent()' a 'Location' y usarlos en su lugar, pero no te gana nada en seguridad. Los métodos Getter y Setter se usan principalmente en Java y C++ para evitar que las personas entren en subclases sin soporte. La cultura python es más educada y confía en los demás, pero haz cosas locas bajo tu propio riesgo. –

+0

No me di cuenta de eso, y estaba imitando el paradigma getter/setter de C++/Java con Python. Aún así, aunque no tengo problemas para acceder a la tabla de búsqueda de otra clase, estoy un poco preocupado. Por ejemplo, cualquier cambio en el comportamiento deseado de la tabla de búsqueda requerirá cambios en el código en todo mi proyecto, y no solo en 'ubicación de la clase'. – max

0

Disculpe, no entiendo el problema. "" "Cuando el agente decide dónde moverse, comprueba para asegurarse de que no se encuentre con otros agentes" "". Evidentemente, una ubicación puede tener 0 o 1 agente. Seguramente la clase Location tiene un atributo de agente.

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() 
+0

En mi caso, el "registro" principal de agentes es simplemente una 'lista' dentro del objeto' mundo'. En su código, el "registro" principal de los agentes es por ubicación, por lo que mi problema está realmente resuelto. Pero luego surgen los otros problemas: ¿cómo hacerlo? Repito rápidamente a través de todos los agentes? En su solución, la pregunta es, ¿dónde guardo esa tabla de búsqueda secundaria que permite la iteración rápida a través de agentes? – max

2

Está bien, creo que puedo dar mi respuesta, que es tal vez más una opinión que una definitiva "hacer esto" (no tengo ningún entrenamiento formal en la programación).

Creo que agent_locations debería ser un miembro de cada instancia de World.

Intento pensar en términos de interfaz principalmente. En mi opinión, la clase mundial debería ser responsable de gestionar los recursos de su mundo, en este caso el espacio. Dado que World es el administrador de espacio, los agentes deben solicitar su mundo si hay espacio disponible (es decir, desocupado), no entre ellos. Por lo tanto, creo que su llamada self.location_is_good sería más apropiada self.world.is_location_available(x, y) [1]

Esto hace que sea natural para el mundo ser responsable de buscar la disponibilidad del espacio dado. La clase mundial también podría tener otras variables que decidan si un espacio está disponible. ¿Qué pasa si hay un arbusto allí? O algo. Probablemente ya tenga algún tipo de tabla para sus coordenadas (x, y) en cada mundo. Estar "ocupado" puede ser una propiedad de esos objetos.

Además: Su mundo ya conoce el estado de cada agente (por [(agent.x, agent.y) for agent in self.agents] [2]). El dict agent_locations es esencialmente un índice o caché para estas propiedades, que por lo tanto pertenece a World.

En cuanto al dolor de enviar el estado de vuelta al World ... bueno, no vas a resolver eso haciendo Agent hazlo en su lugar.Pero hacer update_agent_location(self, agent, x, y) es completamente superfluo, ya que x == agent.x; y == agent.y (si invierte las líneas donde lo llama). Simplemente puede tener un método en World, update_agent_state(self, agent), que World puede usar para actualizar sus índices. Incluso puede enviar un parámetro adicional para describir el tipo de cambio de estado (si no desea actualizar todas las propiedades cada vez).

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) 

Algo así (leer mis notas al pie).

[1] A menos que, por supuesto, la "bondad" de una ubicación dependa de otras variables que si el espacio es libre. P.ej. si solo debe moverse a (x, y) si 1) la ubicación está disponible y 2) el agente tiene 1000 $ para pagar un boleto, entonces debe tener un Agent.can_move_to(x, y) que a su vez llama al método mundial y verifica su billetera .

[2] Supongo que su self.agents = {} es un error tipográfico, ya que no puede append en un dict. ¿Te refieres a una lista ([]) verdad?

+0

1) Gracias, corrigió el error tipográfico. 2) 'update_agent_location' no es redundante porque necesita conocer las coordenadas anterior y nueva (de lo contrario, no puede eliminar el registro anterior).3) Estoy de acuerdo en que no estoy resolviendo ninguna molestia de codificación moviéndome a la clase 'Agent', pero al menos todo queda contenido en una clase, por lo que es más fácil asegurarse de que sea consistente. 4) Me gusta su idea de que el mundo gestiona el espacio, por lo que 'location_available' debe estar en' world'. 5) También estoy pensando en un punto de vista alternativo, donde todo en mi mundo es una base de datos orientada a objetos, donde las búsquedas encajan muy bien. – max

Cuestiones relacionadas