On Tue, 10 Nov 2009 03:40:11 am Björn Lindqvist wrote:
2009/11/6 Raymond Hettinger <python@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