Steven D'Aprano wrote:
On Sat, 24 Oct 2009 08:12:26 pm Martin (gzlist) wrote:

There's a different proposed meaning for `set.get` that's been
discussed on python-dev before:

<http://mail.python.org/pipermail/python-dev/2009-April/088128.html>

For that case, I think the OP was mistaken mistaken to reject using a dict. He had objects with a key and wanted an index based on that key.

That thread has an example of a use-case for extracting an item from a set, given the item itself: interning.

The current Python idiom for interning is to use a dict:

# store the canonical version
_cache[item] = item

# retrieve the interned version
item = _cache[item]

This is an interesting use case as it turns on the difference between mathematics, where value is identity, and informatics, where the two concepts split. The above is meaningless in math, but *is* meaningful in informatics, where different objects can have the same set-membership value. For Python's builtin sets, set-membership 'value' is based on hash comparisons followed by equality comparison, rather than identity. The purpose of this is to make them more like mathematical sets.

But because of the value-identity distinction, they are not exactly like mathematical sets. Given that set()s implicitly map equivalence classes (which are not Python objects) to representitive members of that class (which are), then I agree and would even argue that there should be method of retrieving the one representative given any member.

(The above assumes that .__eq__ is properly defined for potential members so that they are properly partitioned into equivalence classes. This is not true when Decimals are mixed with other number classes.)

A counter-argument is that the implicit mapping is an implementation detail. A bit-mapped set class for a finite range of ints would not have this mapping. So an ABC for sets probably should not include such a method.

It has been argued that such interning is better done by sets rather than dicts, since this will save a pointer per item (plus any additional overhead dicts may have over that of sets). If this argument is accepted, then we want a fast, efficient operation to perform this:

def get(self, item):
    """Return an element of the set equal to item.
Useful for retrieving the canonical version of an element and for interning.
    """
    for x in self:
        if x == item:
            return x
    raise ValueError('item not in set')


The above is O(N); ideally it should be O(1).

Normally we don't care about identity, only equality, so if x and item above are equal we are indifferent about which one we use. But interning is a real example of a use-case where we do care about identity. Arguably it is inefficient and wasteful to use a dict for interning when sets could be just as fast while using less memory.

The other proposed semantics for set.get() are to retrieve an arbitrary element. It must be arbitrary, because elements in a set are unordered and unkeyed. This gives us the semantics of pop() without removing the element:

def get(self):
    """Return an arbitrary element of the set without removing it."""
    for x in self:
        return x
    raise ValueError("empty set")

This is a frequent request, but I still don't know what the use-case is.

If you'll excuse me stating the obvious, both of these can easily be written as helper-functions. The main arguments for making them methods are:

(1) The first one is needlessly O(N) when it could be O(1).

(2) The second one is apparently a common need (although I personally have never needed it), forcing people to re-invent the wheel, sometimes incorrectly or inefficiently, e.g.:

def get(aset):
    for element in aset:
        pass
    return element

Terry Jan Reedy


_______________________________________________
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com

Reply via email to