Reputation: 42060
Suppose I have an iterator, and I want to add some elements before or after it. The only way I can think of to do this is to use an explicit loop:
def myiter(other_iter):
yield "First element"
for item in other_iter:
yield item
yield "Last element"
Is there a better or more efficient way to do this? Is there a function with a name like yield_items_from
that can be used like this?
def myiter(other_iter):
yield "First element"
yield_items_from(other_iter)
yield "Last element"
Ok, I oversimplified my example. Here's a better one:
Suppose I have an iterator other_iter that returns an ascending sequence of nonnegative integers. I want to return an iterator that counts up from zero, returning 1 for numbers returned by other_iter and 0 otherwise. For example, if other_iter yields [1,4,5,7], I want to yield [0,1,0,0,1,1,0,1]. Is there an efficient and readable way to do this?
Upvotes: 2
Views: 499
Reputation: 275
As of Python 3.3, you can use the yield from
syntactic form.
def myiter(other_iter):
yield "First element"
yield from other_iter
yield "Last element"
See PEP 380 -- Syntax for Delegating to a Subgenerator for details, or the What's New in Python 3.3 post for a brief tutorial.
Upvotes: 2
Reputation: 104014
Itertools works, but if you want an alternate:
def ch(*iters):
for it in iters:
for elem in it:
yield elem
s1=["First element"]
s2=['middle 1', 'middle 2', 'middle 3']
s3=["last element"]
for i in ch(s1,s2,s3):
print i
Update from your Edit
Your specific request can be met with:
def in_l(l):
count = 0
while count<=max(l):
if count in l:
count += 1
yield 1
else:
count += 1
yield 0
print list(in_l([1,4,5,7]))
Be aware that l
has to be a sequence, not a generator object. Sequences have a length and a max. Generators may be infinite and therefor no length, subscript, of calculable max value.
I think there is more to this puzzle? What you are describing sounds like there are many other design patterns that better implement it. Have you looked at a simple state machine?
Upvotes: 0
Reputation: 880269
Use itertools.chain:
import itertools
for item in itertools.chain(["First element"],other_iter,["Last element"]):
...
Regarding the edited question, how about:
import itertools as it
def bincounter(iterable):
counter=it.count()
for elt in iterable:
for c in counter:
if c < elt:
yield 0
else:
yield 1
break
other_iter=iter([1,4,5,7])
print(list(bincounter(other_iter)))
# [0, 1, 0, 0, 1, 1, 0, 1]
Upvotes: 2
Reputation: 107666
To answer your new question:
other_iter = [1,4,5,7]
def flags( items ):
c = 0
for this in items:
while c < this:
yield 0
c += 1
yield 1
print list(flags(other_iter))
I'm pretty sure there is no builtin or helper that does this.
Upvotes: 0
Reputation: 24802
No, there is nothing like yield_items_from
, although there is a draft proposal for adding one to Python 3.X in PEP 380.
For current Python, the explicit loop is the only way to yield from a sub-iterator. However, it's reasonably efficient, and it is the idiomatic way to do it, so you shouldn't be too bothered by the verbosity.
If all you need to do is add new items to the front or back of the iterator, then you can simply use itertools.chain
to create a new iterator. You can use it to chain several iterators together, or append/prepend individual items by wrapping them in a list.
new_iter = itertools.chain(['prefix item'], old_iter, appended_iter)
Upvotes: 3