[Tutor] indexing with a class

Terry Carroll carroll at tjc.com
Sat Feb 28 15:23:26 EST 2004

On Fri, 27 Feb 2004, Christopher Spears wrote:

> I am trying to create a class in the UserDict module
> that will remember the order in which I enter keys. 
> Here is an example of an attempt to solve this
> problem:
> class ODict(UserDict):
>     def __setitem__(self, key, item):
>         counter = 0
>         self.data[key] = item
>         counter = counter + 1
>         self.order = [(key,counter)]
> The idea was to have the class generate a list of
> tuples that have the key and the order that key was
> entered, i.e. [('key1',1),('key2',2),...etc].
> But when I went for a test run, I got this result
> (UserDict2 is a copy of the UserDict module.  I wanted
> to work with a copy instead of the original for
> safety.):
> >>> import UserDict2
> >>> dict = {}
> >>> a = UserDict2.ODict(dict)
> >>> a['test'] = 1
> >>> a.order
> [('test', 1)]
> >>> a['another test'] = 2
> >>> a
> {'test': 1, 'another test': 2}
> >>> a.order
> [('another test', 1)]

This looks like what you'd expect.

Let's run through this.  First time through _setitem_:

>     def __setitem__(self, key, item):
key is "test", "item" is 1.  Other variables (counter, self.order) are not 
yet in use.

>         counter = 0
counter is 0
>         self.data[key] = item
self.data["test"] is 1; that is:
 self.data is {"test":1}
>         counter = counter + 1
counter is 1
>         self.order = [(key,counter)]
self.order is [("test",1)]

Now you reach the end.

Second time:

>     def __setitem__(self, key, item):
key is "another test"; item is 2.  The variable counter is not yet in use, 
and self.order still has [("test",1)] from last time.
>         counter = 0
counter is 0
>         self.data[key] = item
self.data["another test"] is 2; that is:
 self.data is {"test":1, "another test":2}

>         counter = counter + 1
counter is 1
>         self.order = [(key,counter)]
self.order is [("another test", 1)]

It looks to me like you have a few thing here; first, you expect, counter 
to retain its value between calls.  It won't.  But note that the 
self.variables do.  If you really want to retain variable values across 
invocations, they need to be variables within the object itself.  The best 
way to do this is in an __init__ method, which you do not show.

Second, you overwrite your list of tuples in each run, instead of
modifying it.  You should probably use list.append() to append a new
entry.  Of course, you can't append to a list that doesn't exist, so your
first run-through, when the list needs to be created, is problematic. The
way I would go at this is to initialize it to an empty list in __init__,
and append to it in the __setitem__.

As an aside, I don't see why you'd want to maintain a counter and put that 
into the list anyway.  The way you're looking at it, the first entry would 
have "1" for the stored count, the second "2", the third "3", etc.  Why 
store the count?  If you'll need to give back the items by the order in 
which they were added, it is better (and easier, too) to just use the 
order of the list.  So, your "order" list should just end up like this:

["test", "another test", "yet another test"], etc.

Finally, this line of yours scares me:

> (UserDict2 is a copy of the UserDict module.  I wanted
> to work with a copy instead of the original for
> safety.):

I am pretty sure that the nature of your assignment is *not* to take the 
UserDict.py file and modify it.  I'm willing to be that, when the 
assignment says to "use the UserDict module" (or whatever the exact words 
were, that your instructor meant to use it to subclass from.  This seems 
pretty clear from the hint that __setitem__ should be overridden (as 
opposed to being modified.

I strongly suggest you seek clarification on this from your instructor.  
I'm pretty sure he or she is not expecting you to take the Python UserDict
module and edit it.

In general, if you're subclassing form an existing module, (say you're 
writing a class bar based on the existing class foo) is something like 

def bar(foo):
   def __init__(self, other params):
      foo.__init__(self, other params)  # invoke the superclass's init
      [all my additional __init__ type stuff goes here]
   def __another_method__(self, other params)
      foo.__another_method__(self, other params) 
      # invoke superclass's method

For your case, I suggest your definitions look something like this:

def ODict(UserDict.UserDict):
   def __init__(self, dict=None, **kwargs)
      UserDict.UserDict.__init__(self, dict=None, **kwargs)
   def __setitem__(self, key, item):
      UserDict.UserDict.__setitem__(self, key, item)

I hope this helps.

Terry Carroll
Santa Clara, CA
carroll at tjc.com
Modell delendus est

More information about the Tutor mailing list