1

Is there any way to make a list of classes behave like a set in python?

Basically, I'm working on a piece of software that does some involved string comparison, and I have a custom class for handling the strings. Therefore, there is an instance of the class for each string.

As a result, I have a large list containing all these classes. I would like to be able to access them like list[key], where in this case, the key is a string the class is based off of (note: the string will never change once the class is instantiated, so it should be hashable).

It seems to me that I should be able to do this somewhat easily, by adding something like __cmp__ to the class, but either I'm being obtuse (likely), or I'm missing something in the docs.

Basically, I want to be able to do something like this (Python prompt example):

>>class a:
... def __init__(self, x):
...  self.var = x
...
>>> from test import a
>>> cl = set([a("Hello"), a("World"), a("Pie")])
>>> print cl
set([<test.a instance at 0x00C866C0>, <test.a instance at 0x00C866E8>, <test.a instance at 0x00C86710>])
>>> cl["World"]
<test.a instance at 0x00C866E8>

Thanks!

Edit Some additional Tweaks:

class a:
... def __init__(self, x):
...     self.var = x
... def __hash__(self):
...     return hash(self.var)
...
>>> v = a("Hello")
>>> x = {}
>>> x[v]=v
>>> x["Hello"]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'Hello'
>>> x["Hello"]
3
  • A SET uses indexes, not keys. A dictionary seems to be more what you want, but you're using a passing in a key of a("Hello"), and trying to retrieve it using a different value -- "Hello". Commented Apr 5, 2010 at 10:08
  • You should also define __cmp__ method for class a which will be called in the case of hash value collisions. Commented Apr 5, 2010 at 10:18
  • Please use UpperCase Names for Classes. Please inherit from object. Like this: class A(object). It will make many things easier for us to read and easier for you to figure out, too. Commented Apr 5, 2010 at 10:23

5 Answers 5

2

Just write a class that behaves a bit like a mapping:

class ClassDict(object):
  def __init__(self):
    self.classes = {}

  def add(self, cls):
    self.classes[cls.__name__] = cls

  def remove(self, cls):
    if self.classes[cls.__name__] == cls:
      del self.classes[cls.__name__]
    else:
      raise KeyError('%r' % cls)

  def __getitem__(self, key):
    return self.classes[key]

  def __repr__(self):
    return 'ClassDict(%s)' % (', '.join(self.classes),)

class C(object):
  pass

class D(object):
  pass

cd = ClassDict()
cd.add(C)
cd.add(D)

print cd

print cd['C']
Sign up to request clarification or add additional context in comments.

3 Comments

Hmmmm. It seems like it should work, but when I try to evaluate key in ClassDict it fails with a keyerror, and a print statement in the __getitem__ function shows it's passing a key of 0.
Doh - Need to add __contains__
I wound up doing this, since it let me handle some data deduplication issues I was having as well. OTOH, I now have run into problems with the 2gb 32bit memory limit. *sigh*
2

Why don't you just do:

>>> v = MyStr("Hello")
>>> x = {}
>>> x[v.val]=v
>>> x["Hello"]
MyStr("Hello")

Why go through all the trouble of trying to create a hand-rolled dict that uses different keys than the ones you pass in? (i.e. "Hello" instead of MyStr("Hello")).

ex.

class MyStr(object):
    def __init__(self, val):
        self.val = str(val)

    def __hash__(self):
        return hash(self.val)

    def __str__(self):
        return self.val

    def __repr__(self):
        return 'MyStr("%s")' % self.val


>>> v = MyStr("Hello")
>>> x = {}
>>> x[str(v)]=v
>>> x["Hello"]
MyStr("Hello")

Comments

1

Set and dict use the value returned by an object's __hash__ method to look up the object, so this will do what you want:

>>class a:
... def __init__(self, x):
...  self.var = x
...
... def __hash__(self):
...  return hash(self.var)

2 Comments

When class a's __hash__(self) returns the same hash as the string x, you get the additional benefit of it making no difference whatsoever whether you do a dict lookup on a(x) or x.
Ok, but how do I insert the class into the dictionary? With __hash__ defined, trying to add an instance of the class to a dictionary either gets me a syntax error, or a entry in the dict with the key being the class itself.
1

As I remember "set" and "dict" uses also __hash__

From Python 2.x doc:

A dictionary’s keys are almost arbitrary values. Values that are not hashable, that is, values containing lists, dictionaries or other mutable types (that are compared by value rather than by object identity) may not be used as keys.

Comments

0

Do you want something like this

class A(object):
    ALL_INSTANCES = {}
    def __init__(self, text):
        self.text = text
        self.ALL_INSTANCES[self.text] = self



a1 = A("hello")
a2 = A("world")

print A.ALL_INSTANCES["hello"]

output:

<__main__.A object at 0x00B7EA50>

Comments

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.