[Python-Dev] Retrieve an arbitrary element from a setwithoutremoving it
chris at subtlety.com
Thu Nov 5 21:43:32 CET 2009
On Wed, Nov 4, 2009 at 7:07 PM, Raymond Hettinger <python at rcn.com> wrote:
> [Steven D'Aprano]
>>> Anyway, given the level of opposition to the suggestion, I'm no longer
>>> willing to carry the flag for it. If anyone else -- perhaps the OP --
>>> feels they want to take it any further, be my guest.
I feel pretty strongly that it's a wart in the language, and a
sufficiently strong one that it should be remedied. I'm happy to
champion it, but haven't the faintest idea what that entails.
> Summarizing my opposition to a new set method:
> 1) there already are at least two succinct ways to get the same effect
> 2) those ways work with any container, not just sets
> 3) set implementations in other languages show that this isn't needed.
> 4) there is value to keeping the API compact
> 5) isn't needed for optimization (selecting the same value in a loop makes
> no sense)
> 6) absence of real-world code examples that would be meaningfully improved
> I would be happy to add an example to the docs so that this thread
> can finally end.
Adding an example to the docs does not solve the problem, which is
if you come across the following code:
for x in s:
... it really looks like it does nothing. It's only because of the
slightly idiosyncratic way Python handles variable scoping that it has
an effect at all, and that effect isn't overtly related to what the
code says, which is "Iterate over all the elements in this set, then
immediately stop after the first one". s.get() or s.pick() are both
more succinct and more clear, saying "Get me an arbitrary element from
this set". To make matters worse, "for x in s: break" fails silently
when s is empty, and "x = iter(s).next()" raises a StopIteration
exception. Neither is clear.
The obvious way, for newcomers, of achieving the effect is:
x = s.pop()
... and that's simply horrible in terms of efficiency. So the
"obvious" way of doing it in Python is wrong(TM), and the "correct"
way of doing it is obscure and raises misleading exceptions.
I suppose, mulling things over, the method should be called
.pick(), which avoids any confusion with .get(). And, as I've stated,
I think it should return a member of the set, with no guarantees what
member of the set is returned. It could be the same one every time,
or a random one, or the last one placed in the set.
For cases where people want to cycle through the members of the set
in a predictable order, they can either copy the contents into a list
(sorted or unsorted) *or* subclass set and override the .pick() method
to place stronger guarantees on the API.
So, summarizing my responses:
1) the two succinct ways are unclear and not immediately obvious
2) the existing methods aren't needed for other objects
3) set implementations in other languages are irrelevant
4) this is a small, targeted change which not make the API disordered or unruly
5) could very well be needed for optimization, in cases where
constructing an iterator is expensive
6) there have been several real-world examples posted which would be
improved by this change
More information about the Python-Dev