user648852
user648852

Reputation:

Generator with slice assignment

Suppose I have a silly function, like FizzBuzz:

>>> def FizzBuzz(i):
...     if i % 15 == 0:
...         return "FB"
...     elif i % 3 == 0:
...         return "F"
...     elif i % 5 == 0:
...         return "B"
...     else:
...         return i 

And a list:

>>> li=list(range(1,22))
>>> li
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21]

Now if I do a generator expression applying that function to the list:

>>> ge=(FizzBuzz(x) for x in li)
>>> ge
<generator object <genexpr> at 0x1063ca3a8>

I can turn that into a list (i.e., run the generator expression):

>>> list(ge)
[1, 2, 'F', 4, 'B', 'F', 7, 8, 'F', 'B', 11, 'F', 13, 14, 'FB', 16, 17, 'F', 19, 'B', 'F']

Now suppose I do a slice assignment rather than an assignment to a separate name:

>>> li=list(range(1,22))
>>> li[:]=(FizzBuzz(x) for x in li)
>>> li
[1, 2, 'F', 4, 'B', 'F', 7, 8, 'F', 'B', 11, 'F', 13, 14, 'FB', 16, 17, 'F', 19, 'B', 'F']

You can see that the generator expression is immediately executed.

Same with map:

>>> map(FizzBuzz, li)
<map object at 0x10640c358>
>>> li[:]=map(FizzBuzz, li)
>>> li
[1, 2, 'F', 4, 'B', 'F', 7, 8, 'F', 'B', 11, 'F', 13, 14, 'FB', 16, 17, 'F', 19, 'B', 'F']

Now suppose that FizzBuzz is not a silly function, but rather a function that takes a while and li is big enough that you want to do slice assignment.

Is there a way to defer the execution of the generator but still assign in place? (I do realize that I can just do ge=(f(x) for x in big_list); big_list[:]=list(ge) which is a workaround...)

Upvotes: 0

Views: 189

Answers (1)

Alex Martelli
Alex Martelli

Reputation: 881775

Clearly, (FizzBuzz(x) for x in li) needs li to be still "intact" when it starts running, since it must loop over it and get the right items.

So where would you want to "assign in place" -- what place do you have in mind?

Assigning it to a variable independent of li, which you diss as "a workaround", is fine of course.

But where would you like to put it instead?!

Certainly not anywhere where it would trample on li's contents until you're ready to run it, since we've already established that said contents must be intact when the generator starts running.

You could bind those contents with a def, e.g

def doge(savedli=list(li)):
    for x in savedli: yield FizzBuzz(x)

Now, li[:]=doge will wipe away li's contents, superficially, but still keep them safely stashed away in doge (now set to li[0]), so later you could do li[:] = doge(). But I don't see any advantage in this more contorted approach.

So if this answer is not satisfactory, please clarify in what place you want to "assign in place" a generator, respecting of course the need for the list's original contents to survive somewhere until the generator starts running.

Upvotes: 1

Related Questions