8

so i have this gen object:

yld123cu = (x * u for x in range(1, 4))

which i can only use like so:

u = 2  # some user given value
for i in yld123cu:
    print(i, end=' ')

is there any syntax to avoid the extra line with u=.. assignment? any way to put the u value inside the for statement?

i'd want something similar to below but using gen expression, not function:

def yld123u(u):
    for i in range(1, 4):
        yield i * u

for i in yld123u(2):
    print(i, end=' ')
harry777
  • 139
  • 1
  • 7
  • 3
    "id want something similar to below but using gen expression, not function:" -- why? It's a perfectly valid and working solution. And behind the scenes there is more or less no difference between the two – Dunes Mar 19 '19 at 18:36
  • 2
    thank you guys for every reply! learned something from each of them today. really appreciate it, amazing website)) – harry777 Mar 21 '19 at 05:40

3 Answers3

16

Since a generator expression cannot have its own scope, the preferred approach would be to define a generator function as you suggested.

def yld123u(u):
    for i in range(1, 4):
        yield i * u

If you need to update u along the way and this is the reason you want to avoid the above, then know that you can send a value to a generator.

def yld123u(u):
    for i in range(1, 4):
        received = yield i * u # a sent value will be assigned to 'received'
        u = u if received is None else received

Examples

The generator still behaves the same if you do not use send.

for x in yld123u(4):
    print(x)

Output

4
8
12 

You can use send to update the value of u.

gen = yld123u(4)
v0 = next(gen)
v1 = gen.send(0)
v2 = gen.send(4)
print(v0, v1, v2)

Output

4 0 12

Input loop

If you need the generator to interact with user input, here is a past answer of mine which suggest a way to implement a yield-receive generator usable in a for-loop.

class YieldReceive:
    stop_iteration = object()

    def __init__(self, gen):
        self.gen = gen
        self.next = next(gen, self.stop_iteration)

    def __iter__(self):
        return self

    def __next__(self):
        if self.next is self.stop_iteration:
            raise StopIteration
        else:
            return self.next

    def send(self, value):
        try:
            self.next = self.gen.send(value)
        except StopIteration:
            self.next = self.stop_iteration

Usage

it = YieldReceive(yld123u(4))
for x in it:
    print(x)
    it.send(int(input('Enter next u value: ')))
Olivier Melançon
  • 21,584
  • 4
  • 41
  • 73
  • 5
    hey Olivier, thank you so much for fantastic insight and details! and the link for the send method too. been playing all day with pycharm debugger wrapping my head around it.. so gen functions + send/next are the way to go, okey. i've only been studying python a few weeks now after c-family, just got to the oop part yesterday so really enjoyed stepping through your class sample too. its such noble work you're doing so generously helping people, all the best to you!! – harry777 Mar 21 '19 at 05:39
2

You can use lambda. This works for me:

yld123cu = lambda u: (x * u for x in range(1, 4))

for i in yld123cu(2):
    print(i, end=' ')
Uli Sotschok
  • 1,206
  • 1
  • 9
  • 19
1

You can try like this.

Note: Here, we don't have a problem of using named lambda function.

for i in (lambda u: (x * u for x in range(1, 4)))(u=2): 
    print(i, end=' ')

or

for i in (lambda u: (x * u for x in range(1, 4)))(2): 
    print(i, end=' ')

» Output

2 4 6 
hygull
  • 8,464
  • 2
  • 43
  • 52