0

I have a class which represents an object to be kept in a set. I would like the class itself to remember how many it has created so that when you call SetObject() and __init__() a new object is created, which receives a unique index. Maybe something like this

class SetObject(object):
  # static class variable 
  object_counter = 0

  def __init__(self, params):
    self.params=params
    self.index = self.get_index()

  def get_index(self):
    object_counter += 1
    return object_counter-1

a = SetObject(paramsa)
b = SetObject(paramsb)
print a.index
print b.index

would produce

0
1

or something like this. Currently it seems that this approach gives a "variable referenced before assignment" error.

2
  • Remember that objects need to be hashable if they go in a set, and they may not be mutable. Commented Dec 17, 2012 at 8:58
  • Not actually true. The state used to compute the object's eq and hash must be immutable. If you don't override a class'eq and hash than the object's id is used to deduce the hash. And since the object's id never changes (even if it's attributes change) => you can use it in a set without any problems. Commented Dec 17, 2012 at 10:54

3 Answers 3

3

You need to write:

  def get_index(self):
     SetObject.object_counter += 1
     return SetObject.object_counter-1

otherwise it would only work if object_counter was a global variable.

Sign up to request clarification or add additional context in comments.

Comments

2

You need to use a reference to the class to refer to it's variables; you could perhaps use a class method (with the @classmethod decorator), but there is really no need to.

Better use itertools.count() to get a fool-proof 'static' counter; no need to reassign back to the class attribute then:

import itertools

class SetObject(object):
   object_counter = itertools.count().next

   def __init__(self, params):
       self.params=params
       self.index = self.object_counter()

(code above assumes Python 2; on Python 3 iterables do not have a .next method and you'd need to use functools.partial(next, itertools.count()) instead).

Because the counter is an iterator, we don't need to assign to SetObject.object_counter at all. Subclasses can provide their own counter as needed, or re-use the parent class counter.

3 Comments

Why not just use self.index = self.object_counter()? The class attribute is found automatically when there's no overriding instance attribute.
I'm not complaining about assigning to self.index. All I'm saying is that calling self.__class__.object_counter() is (in the absence of an instance attribute) exactly the same as the simpler self.object_counter()
@Duncan: Duh. Of course. Updated, thanks for waking me up this morning (need more caffeine, I guess).
1

The line

object_counter += 1

translates to

object_counter = object_counter + 1

When you assign to a variable inside a scope (e.g. inside a function), Python assumes you wanted to create a new local variable. So it marks object_counter as being local, which means that when you try to get its value (to add one) you get a "not defined" error.

To fix it, tell Python where to look up object_counter. In general you can use the global or nonlocal keywords for this, but in your case you just want to look it up on the class:

self.__class__.object_counter += 1

3 Comments

I think SetObject.object_counter is cleaner than self.__class__.object_counter
@IoanAlexandruCucu I don't: it hardcodes and duplicates the name of the class. That breaks inheritance (a class LoggedSetObject(SetObject), say) and refactoring (if you want to rename SetObject).
+1 for explaining what actually happens. But nonlocal only works in python 3, OP seems to be using python 2.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.