[ACCEPTED]-select single item from a collection : Python-generator
A simpler solution is to use tuple unpacking. This 3 will already do everything you want, including 2 checking that it contains exactly one item.
Single 1 item:
>>> name, = (name for name in ('bob','fred') if name=='bob')
>>> name
'bob'
Too many items:
>>> name, = (name for name in ('bob','bob') if name=='bob')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: too many values to unpack
No items:
>>> name, = (name for name in ('fred','joe') if name=='bob')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: need more than 0 values to unpack
For those using or interested in a third-party 3 library, more_itertools
implements such a tool with native 2 error handling:
> pip install more_itertools
Code
import more_itertools as mit
mit.one(name for name in ("bob", "fred") if name == "bob")
# 'bob'
mit.one(name for name in ("bob", "fred", "bob") if name == "bob")
# ValueError: ...
mit.one(name for name in () if name == "bob")
# ValueError: ...
See more_itertools
docs for details. The underlying source code is 1 similar to the accepted answer.
Simple approach:
print (name for name in ('bob', 'fred') if name == 'bob').next()
If you really want an error 3 when there is more than one value, then 2 you need a function. The most simple I can 1 think of is (EDITED to work with lists too):
def one(iterable):
it = iter(iterable)
val = it.next()
try:
it.next()
except StopIteration:
return val
else:
raise Exception('More than one value')
Have a look into the itertools.islice() method.
>>> i2=itertools.islice((name for name in ('bob','fred') if name=='bob'),0,1,1)
>>> i2.next()
'bob'
>>> i2.next()
Traceback (most recent call last):
File "<interactive input>", line 1, in <module>
StopIteration
>>>
This module 15 implements a number of iterator building 14 blocks inspired by constructs from the Haskell 13 and SML programming languages. Each has 12 been recast in a form suitable for Python.
The 11 module standardizes a core set of fast, memory 10 efficient tools that are useful by themselves 9 or in combination. Standardization helps 8 avoid the readability and reliability problems 7 which arise when many different individuals 6 create their own slightly varying implementations, each 5 with their own quirks and naming conventions.
The 4 tools are designed to combine readily with 3 one another. This makes it easy to construct 2 more specialized tools succinctly and efficiently 1 in pure Python.
Do you mean?
def one( someGenerator ):
if len(list(someGenerator)) != 1: raise Exception( "Not a Singleton" )
What are you trying to accomplish 1 with all the extra code?
Here is my try at the one()
function. I would 2 avoid the explicit .next()
call and use a for loop 1 instead.
def one(seq):
counter = 0
for elem in seq:
result = elem
counter += 1
if counter > 1:
break
if counter == 0:
raise Exception('No values')
elif counter > 1:
raise Exception('Too many values')
return result
First, (to answer the actual question!) your 7 solution will work fine as will the other 6 variants proposed.
I would add that in this 5 case, IMO, generators are overly complicated. If 4 you expect to have one value, you'll probably 3 never have enough for memory usage to be 2 a concern, so I would have just used the 1 obvious and much clearer:
children = [name for name in ('bob','fred') if name=='bob']
if len(children) == 0:
raise Exception('No values')
elif len(children) > 1:
raise Exception('Too many values')
else:
child = children[0]
How about using Python's for .. in syntax 1 with a counter? Similar to unbeknown's answer.
def one(items):
count = 0
value = None
for item in items:
if count:
raise Exception('Too many values')
count += 1
value = item
if not count:
raise Exception('No values')
return value
More Related questions
We use cookies to improve the performance of the site. By staying on our site, you agree to the terms of use of cookies.