May 18, 2009

Simple Object Interactions

Here's a relatively simple piece of code that shows how easy it is to have Python objects interact. It's fairly standard stuff, so it probably won't be interesting to old hands, but if you haven't been using object-oriented programming very long then it might make a point or two.

Note that the code is in Python 3, though it will actually run on earlier versions, the printing will just be slightly different.
#
# Calling all reactive agents (with apologies to William Burroughs)
#
class Simulation:

def __init__(self, howmany):
self.agents = []
for i in range(howmany):
self.agents.append(Agent(self, i))

def showImportant(self, agent):
return "Agent: %d simulation: %d" % (agent.number, id(self))

def listAgents(self):
for a in self.agents:
a.showMe()

class Agent:

def __init__(self, sim, number):
self.sim = sim
self.number = number

def showMe(self):
print("Agent", self.number, "reporting:")
result = self.sim.showImportant(self)
print(result)

s = Simulation(3)
s.listAgents()
So, we start out with a class whose __init__() method is called with one argument, the number of agents to create. It creates a list of that many agents as its agents attribute. Note that when the Agent is created the Simulation instance passes itself as an argument to the Agent creator, and the Agent.__init__() method saves the reference to the Simulation instance as the Agent's sim instance variable.

Colloquially we could say that each Agent "knows" which Simulation it's a part of. So an Agent is able to call the methods of the simulation that it's a part of. This makes it possible to devise agents that call various simulation methods, and to incorporate them in several different simulations which implement those methods differently.

When building large data structures with this kind of pattern, by the way, it's important to note that the references are circular (simulations refer to agents, and agents refer to simulations). The garbage collector in older versions of Python would have real difficulties with structures such as the ones created in the code above. In essence it would say to itself "Well, I can't delete the simulation until I have deleted all the agents it refers to." Then it would look at all the agents, and for each one it would say to itself "I can't delete this until I have deleted the simulation it refers to". An unintelligent garbage collection might get stuck in an infinite loop here, but Python's garbage collector has never been that stupid.

Nowadays, I am happy to say, in Python 2 or 3 the collector is able to recognize these cyclic references and, as long as there are no references to such a structure from the outside, will (eventually) reclaim the space.

Next time we'll consider how a simulation might use several types of agents, still providing common behavior for them all.

No comments: