# Factoring Out Common Args To Zipped Generators

I'm playing around with some additive synthesis in Python.

I've implemented an oscillator as a generator that takes a number of parameters. It is then possible to mix multiple oscillators using zip (or better, itertools.izip) over them and doing a (weighted) sum.

However, I wanted to be able to factor out common arguments to the oscillators so I didn't have to specify the frequency of each one individually.

I knew functools.partial would be part of the solution but it took me a while to work out how to combine its use with generators and itertools.izip.

Here is a simplified progression of what I came up with.

### Phase 1

Rather than use oscillators, let's just imagine with have a generator that works a lot like xrange:

```def gen1(start, stop, step):
n = start
while n <= stop:
yield n
n += step
```

then we can combine multiple generators and, say, sum the corresponding elements like this:

```for x in zip(gen1(10, 20, 2), gen1(10, 25, 3)): print sum(x),
```

### Phase 2

Let's abstract this into a function that takes generators as arguments (and uses itertools.izip)

```def mixer1(*generators):
return (sum(x) for x in izip(*generators))

for x in mixer1(gen1(10, 20, 2), gen1(10, 25, 3)): print x,
```

mixer1 is similar to my mixer (although without weighting)

### Phase 3

But now say we wanted to factor out the common start parameter. First we need a partial version of function gen1:

```gen2 = lambda **kwargs: partial(gen1, **kwargs)
```

This allows one to say

```partial_gen = gen2(stop=20, step=2)
```

and then later call

```partial_gen(start=10)
```

to get the generator.

But what we now need is a new version of the mixer that takes the extra keyword args and passes them in to each partial function to turn them back into generators:

```def mixer2(*generators, **kwargs):
return mixer1(*[gen(**kwargs) for gen in generators])
```

and now we can say:

```for x in mixer2(gen2(stop=20, step=2), gen2(stop=25, step=3), start=10): print x,
```

### Phase 4

Here's the final version:

```gen2 = lambda **kwargs: partial(gen1, **kwargs)

def mixer3(*generators, **kwargs):
return (sum(x) for x in izip(*[gen(**kwargs) for gen in generators]))
```

The real thing is a little more involved because of the weighted summing, etc but the hard parts are shown.

The original post was in the category: python but I'm still in the process of migrating categories over.

The original post had 1 comment I'm in the process of migrating over.