tags:

views:

69

answers:

5

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"]
+1  A: 

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.

ony
+1  A: 

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)
Dave Kirby
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`.
ndim
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.
Fake Name
+1  A: 

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']
Ignacio Vazquez-Abrams
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`.
Fake Name
Doh - Need to add `__contains__`
Fake Name
A: 

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>
Anurag Uniyal
+2  A: 

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")
Brendan Abel