2

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"

Edit:

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?

1
  • Ok, all the answers to my edit confirm my belief that an explicit loop (or several) is the only way to go about this kind of thing. Or else have an auxiliary generator that generates generators and use itertools.chain.from_iterable to put them together. Commented Feb 23, 2011 at 19:15

5 Answers 5

3

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)
Sign up to request clarification or add additional context in comments.

1 Comment

That PEP seems to say explicitly that what I want is not currently available in Python, if I understand correctly. The solution I settled on was an auxiliary generator function that returns generators, which I then chain together using itertools.chain.from_iterable.
2

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]

2 Comments

+1 Never write a full iterator/generator when you can use itertools ;) chain(["First element"], other_iter, ["Last element"]).
Ok, what if I want to splice in iterators that I'm generating on the fly (from the contents of an iterator)?
2

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.

Comments

0

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.

Comments

0

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?

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.