[Tutor] class Clowning: saving references to other instances

Rob McGee i812@iname.com
Sat, 15 Dec 2001 01:38:25 -0600

Okay, here's what I have: a class of objects placed on a grid referenced
with X, Y coordinates. Need an example? Here:

from string import uppercase    # letters A to Z

class Clown:
  def __init__(self, location, index, gridSize=16, uppercase=uppercase):
    self.location = location    # an integer, unique
    self.index = index          # also unique integer
    self.name = uppercase[index]  # uppercase letter, also unique
    self.coord = divmod(location, gridSize) # X, Y tuple
  def __str__(self):
    return self.name
  ... and so on

import random

def makeClowns(numClowns=16, gridSize=16):
  gridAvail = range(gridSize**2)  # default grid of 256
  clowns = []
  for index in range(numClowns):
    # select a random number from gridAvail and remove it from the list
    location = gridAvail.pop(random.randrange(len(gridAvail)))
    clown = Clown(location, index)  # create instance
    clowns.append(clown)        # save it in a list
  return clowns                 # return list of Clowns

clowns = makeClowns()

(Those of you who remember my prior posts may notice that I've taken
some of your suggestions. :)

*** the problem ***
There are other methods, including one which calculates the distance
from one Clown instance to another. The distance(self, other) method
uses "exec" to store the distance values in self.(other.name) and in
other.(self.name) (such as "self.A" and "other.Z" for the distance
between the instances named A and Z.) Because it's tied to the way I'm
storing these instances, I don't like the way it works -- if I change
how the data is stored I'll also have to change the class method.

The reason why I'm doing this is just to save a little redundant
processing. The distance() method checks for the existance of the self.A
(or other such attribute) before calculating, and returns that if it
exists. It's all on a pretty small scale (note the default gridSize
value) so it probably doesn't matter much if I were to let it redo the
calculations every time. The distance calculation is not very intense,
probably: just your basic Pythagorean theorem:
    diffX = abs(self.coord[0] - other.coord[0]) # difference in X coord.
    diffY = abs(self.coord[1] - other.coord[1]) # difference in Y coord.
    distance = ((diffX ** 2) + (diffY ** 2)) ** 0.5

One solution would be to put the "exec" code in a function rather than
in the class method, but the class distance() method would still have to
rely on eval() -- and thus on the external data structure! -- to
retrieve the value. Am I right?

Maybe rather than storing all the distances as separate attributes, I
should maintain them all in a single self.distances list:
    [(B, 5.0), (C, 1.414), (D, 7.6645), ...]
Then the distance(self, other) method filters self.distances for
"x[0] == str(other)". If found, return x[1], otherwise calculate the
distance and store it...
    self.distances.append((other.name, distance))
    other.distances.append((self.name, distance))
Simple, no "exec" statement to write nor eval() function to read the
values, and everything's done cleanly within the class methods.

Have I figured out my own solution?!? :) ("Operator, you can forget
about this call ... There's no one there I really wanted to talk
to!" :)[1] Anyway, comments would be appreciated, whether they're other
ideas or confirmation that I'm on the right track.

Really, I figured that out while I was writing this message. It sure
does some good to get away from IDLE every now and then. :)

    Rob - /dev/rob0

[1] from an old Jim Croce song, "Operator", appx. 1971 (?)