[Python-Dev] Retrieve an arbitrary element from asetwithoutremoving it

Steven D'Aprano steve at pearwood.info
Mon Nov 9 21:16:56 CET 2009


On Tue, 10 Nov 2009 03:40:11 am Björn Lindqvist wrote:
> 2009/11/6 Raymond Hettinger <python at rcn.com>:
> > [me]
> >
> >> Why not write a short, fast get_first() function for your utils
> >> directory and be done with it?
> >> That could work with sets, mappings, generators, and other
> >> containers and iterators.
> >> No need to fatten the set/frozenset API for something so trivial
> >> and so rarely needed.
> >
> > Forgot to post the code.  It is short, fast, and easy.  It is
> > explicit about handing the case with an empty input.  And it is
> > specific about which value it returns (always the first iterated
> > value; not an arbitrary one).  There's no guessing about what it
> > does.  It gets the job done.
> >
> > def first(iterable):
> >   'Return the first value from a container or iterable.  If empty,
> > raises LookupError'
> >   for value in iterable:
> >       return value
> >   raise LookupError('no first value; iterable is empty')
> >
> > If desired, it is not hard to change to the last time to return a
> > default value or some other exception.
>
> That function is very nice and genericly lisp-like. Couldn't that one
> be in the builtins? It would both solve the problem and avoid
> fattening the set API.


I'm not sure, but isn't that thread-unsafe?

Because sets aren't directly iterable, `for value in iterable` builds a 
set_iterator operator. If another thread modifies the set after the 
set_iterator is built, but before the value is looked up, you will get 
a mysterious RuntimeError almost impossible to debug.


>>> def first():  # simplified
...     for value in iterator:
...             return value
...
>>> dis.dis(first)
  2           0 SETUP_LOOP              15 (to 18)
              3 LOAD_GLOBAL              0 (iterator)
              6 GET_ITER
              7 FOR_ITER                 7 (to 17)
             10 STORE_FAST               0 (value)

  3          13 LOAD_FAST                0 (value)
             16 RETURN_VALUE
        >>   17 POP_BLOCK
        >>   18 LOAD_CONST               0 (None)
             21 RETURN_VALUE

 
Is it possible for another thread to be called between the GET_ITER and 
STORE_FAST? 




-- 
Steven D'Aprano


More information about the Python-Dev mailing list