set using alternative hash function?

Gabriel Genellina gagsl-py2 at
Thu Oct 15 19:51:44 CEST 2009

En Thu, 15 Oct 2009 13:24:18 -0300, Rami Chowdhury  
<rami.chowdhury at> escribió:

> On Thu, 15 Oct 2009 09:11:00 -0700, Austin Bingham  
> <austin.bingham at> wrote:
>> On Thu, Oct 15, 2009 at 5:15 PM, Gabriel Genellina
>> <gagsl-py2 at> wrote:
>>> En Thu, 15 Oct 2009 11:42:20 -0300, Austin Bingham
>>> <austin.bingham at> escribió:
>>> I think you didn't understand correctly Anthony Tolle's suggestion:
>>> py> class Foo:
>>> ...   def __init__(self, name): = name
>>> ...
>>> py> objs = [Foo('Joe'), Foo('Jim'), Foo('Tom'), Foo('Jim')]
>>> py> objs
>> I understand Anthony perfectly. Yes, I can construct a dict as you
>> specify, where all of the keys map to values with name attributes
>> equal to the key. My point is that dict doesn't really help me enforce
>> that beyond simply letting me set it up; it doesn't care about the
>> values at all, just the keys.
> Perhaps this is an overly naive solution, but could you not define a  
> class that implemented the set interface but used a dict for internal  
> storage, and use that? You'd still have uniqueness (by dict key, which  
> your class would define as object name) and as a bonus, retrievability  
> by name, which set wouldn't give you.

Like this?

 from collections import MutableSet

class KeyedSet(MutableSet):
     """A set with a custom element comparison function."""

     def __init__(self, iterable, key=lambda x: x):
         """Create a KeyedSet from iterable; key function determines  

         `key` must be a callable taking a single argument; it is
         applied to every item in the iterable, and its result
         is used to determine set membership. That is, if key(item)
         returns the same thing for two items, only one of them will
         be in the set."""

         self._items = dict((key(item),item) for item in iterable)
         self._key = key

     # NOT a classmethod because self.key must be transferred too!
     # Fortunately it is always called as self._from_iterable(...)
     # in
     def _from_iterable(self, iterable):
         return type(self)(iterable, key=self._key)

     def add(self, value):
         """Return True if it was added, False if already there."""
         key = self._key(value)
         if key in self._items: return False
         self._items[key] = value
         return True

     def discard(self, value):
         """Return True if it was deleted, False if not there."""
         key = self._key(value)
         if key not in self._items: return False
         del self._items[key]
         return True

     def clear(self):

     def copy(self):
         return type(self)(self._items.values(), key=self._key)

     def __iter__(self):
         return iter(self._items.values())

     def __contains__(self, value):
         try: return self._key(value) in self._items
         except Exception: return False

     def __len__(self):
         return len(self._items)

     def __repr__(self):
         return "%s(%r)" % (type(self).__name__, self._items.values())

def demo():
   class Foo(object):
     def __init__(self, name): = name
     def __repr__(self):
       return "%s(%r)" % (type(self).__name__,

   objs = [Foo('Joe'), Foo('Jim'), Foo('Tom'), Foo('Jim')]
   print objs
   s = KeyedSet(objs, key=lambda
   print len(s), s
   print len(s), s
   moe = Foo('Moe')
   print "moe in s", moe in s
   print "moe in s", moe in s
   print "'moe' in s", 'moe' in s
   s2 = set([Foo('Luc'), Foo('Jim'), Foo('Dan')])
   print "s | s2", s | s2
   print "s & s2", s & s2
   s3 = KeyedSet(s2, key=lambda
   print "s3 - s", s3 - s
   print "s - s3", s - s3
   print "s.copy()", s.copy()


Gabriel Genellina

More information about the Python-list mailing list