0

I have the following code which works. The test_generator function returns a list. But in order to access this list i have to loop twice with 2 for loops as you can see at the bottom. Why i don't get the list item on the first loop for item in test.test_generator(t) and i need 2 loops . Is there a way to get the list values with just one loop ?

class test:
    def __init__(self):
        self.counter = 0
    
    def test_generator(self):
        record = []
        c = 0
        self.counter = self.counter + 1
        if (self.counter <= 50):
            while c < 5: 
                record.append(self.counter + 1)
                c = c + 1
        
        yield record
    
    
t = test()

while  True:
    for item in test.test_generator(t):
        for i in item:
            print(t.counter)
pikk
  • 837
  • 5
  • 21
  • 38
  • You are not using i in your print statement. Is it a typo or is this correct? – Keredu Dec 09 '20 at 13:11
  • 2
    Seems like it should just be `yield self.counter + 1` instead of appending and yielding a list – Tomerikoo Dec 09 '20 at 13:11
  • @Tomerikoo i want to return a list. This is just an example code to explain my question :) – pikk Dec 09 '20 at 13:13
  • 3
    So if you return a list you will have to iterate over it... I don't understand your question. Currently your generator function can just be a regular function with a `return`. To take advantage of the generator you should `yield` the ***elements*** instead of `yield`ing a ***list*** after packing the elements – Tomerikoo Dec 09 '20 at 13:15
  • You should carefully check [what does the "yield" keyword do](https://stackoverflow.com/questions/231767/what-does-the-yield-keyword-do) – Qiu Dec 09 '20 at 13:15
  • 1
    Can you clarify what your expected behaviour is? On the one hand the questions says this is "code which works", implying it is what you want, on the other hand you describe how you have to use it in a way you don't want. What behaviour *would* you desire? Why is the code like this if it does not do what you want? Why is ``test_generator`` a generator if you do not want to iterate over its ``yield``'ed values (of which there is only one)? – MisterMiyagi Dec 09 '20 at 13:20

2 Answers2

0

There seem to be a few misunderstandings shown in your code.

  • You are creating an object instance and then accessing a member function via the class and passing in an instance. Typically you would want to access the function via the instance.
  • Your loops are looping over two different things, which you don't seem to have realised. The outer loop is fetching the list record, which contains five copies of every value of self.counter up to 50. These values are all added to the list at once. The inner loop is iterating over the list of 250 values.
  • Your yield statement is only hit once and is effectively a return.
  • Your code loops forever and on loops after the first one test_generator is just yielding the already-constructed record.

To answer your question directly, you have two loops because the first loop is iterating on your generator (which only actually yields one thing) and the second loop is iterating on the list that you obtained from the generator.

If you wanted to use a generator to yield the counter values it should look more like this:

class test:
    def __init__(self):
        self.counter = 0

    def test_generator(self):
        while self.counter <= 50:
            yield self.counter
            self.counter = self.counter + 1


t = test()

for item in t.test_generator():
    print(item)

See also this answer which goes into detail about how yield works: https://stackoverflow.com/a/231855/3228591

Kemp
  • 3,467
  • 1
  • 18
  • 27
0

You could obtain what you are looking for like this :

class test:
def __init__(self, start = 1):
    self.counter = start
    self.subCounter = 0

def test_generator(self, limit = 50):
    while(self.counter < limit+1): 
        yield self.counter
        if (self.subCounter % 5 == 4): self.counter += 1
        self.subCounter = self.subCounter + 1
        
t = test()
for item in t.test_generator(3):
    print(item) #=> 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3
# OR
list(t.test_generator(3)) #=> [1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3]
dspr
  • 2,383
  • 2
  • 15
  • 19
  • 1
    You've written a generator that only yields one value and might as well be a regular function with a return statement – Kemp Dec 09 '20 at 13:26