#๐ generators and yield
83 messages ยท Page 1 of 1 (latest)
@timber mesa
Remember to:
- Ask your Python question, not if you can ask or if there's an expert who can help.
- Show a code sample as text (rather than a screenshot) and the error message, if you've got one.
- Explain what you expect to happen and what actually happens.
:warning: Do not pip install anything that isn't related to your question, especially if asked to over DMs.
hey ๐๐ป
generators are lazy
next(gen) gives you the first item. each time you call next, you get the next item
what it means that the values aren't calculated all at once
but when u need the value, it's being calculated
to get the second item, you can either call next twice (the second call to next will be the second yielded item)
or you can use a utility like itertools.islice
any reason for not returning a tuple? and iterating that?
is it only supposed to be iterated once?
it is meant so it only produces as many values as you ask for
for example, there are infinite generators
and yeah it's one use
they're right, actually
generators are iterators
yeah but you can only use em once then they exhaust?
i think u need to first understand the iterator properties
did we answer your original question or do you have a followup?
it seems as if we're going in circles or deviating from the actual question
iterators main usage is to lazily iterate
calculate the values only when its needed
this has been pointed out already
you just use it when you don't wanna worry about indexes is all imo
call
next()
next()
next()
then do what ever you want and then starting iterating where you left
i.e 3
item 6 is operation number 6 u can say
operation number 6 comes after operation number 5
i disagree with this. this is rarely the use case for using generators
what is the most popular usecase?
the function itself is technically the iterator object. the yields are the values that are returned via next
lazy iteration
no what
the function itself is a function
no
well either way
i think u're running too fast OP
The function is not an iterator. You call the function to get an iterator
yeah that's my bad, just tested it
!e
def counter():
n = 0
while True:
yield n
n += 1
counter1 = counter()
counter2 = counter()
print(next(counter1), next(counter1), next(counter1))
print(next(counter2), next(counter2))
print(next(counter1), next(counter1))
:white_check_mark: Your 3.13 eval job has completed with return code 0.
001 | 0 1 2
002 | 0 1
003 | 3 4
it seems like you think an iterator is an actual object that things subclass from, but it's just anything that implements both __next__ (i.e able to have next() called on it) and implements __iter__
ye instead of destroying the stack frame it pauses it
for loop just keeps on calling next(gen)
until stopIteration
class MyIterator:
def __iter__(self):
return self
def __next__(self):
return 1
this would be considered an "iterator", even though it doesn't subclass from anything
In this case:
counteris called a "generator function"counter1is a "generator". A generator is a specific kind of iterator.
The point of a generator is to produce values lazily (meaning: on demand), instead of computing all of them and storing them in a list. When you call next(counter1) or do one iteration of a for loop over counter1, just enough code from the generator function is executed until it reaches yield <something>
yes
From a broader perspective, generator functions are a nice way of implementing iterators. You could implement the above with a class that has a manual __next__ implementation: ```py
class Counter:
def init(self):
self.n = 0
def __iter__(self):
return self
def __next__(self):
old_value = self.n
self.n += 1
return old_value
Yes
...this is very simple, but imagine that you have ifs and fors and try-excepts in the generator function. That will require a hellish contraption if you were to replicated that with a manual __next__
Another neat application of generator functions is contextlib.contextmanager (if you're familiar with context managers)
it doesn't have iter though
there's a default implementation of iter?
my bad, fixed
๐ no worries
executing a yield doesn't create a new generator
You created a single generator by calling the counter function. The generator will yield integers
If a function contains the yield keyword, it's a "generator function" and not a normal one
Calling a generator function will just return a generator, yes
Calling the generator function returns a generator, which on its own will not do anything. The generator advances only when you call next() on it.
Look at the counter generator function for example. It has an infinite loop, so it's never going to stop producing values
If counter() waited until all the values were produced, it would never complete. That's the point of generators (or iterators in general): they produce the next item only when you ask for it
An iterator is a very generic thing. This is an iterator for example: #1418378203898318969 message
An iterator just needs to know how to generate a new item, it doesn't need a buffer or anything like that
When you call next(iterator), it just calls iterator.__next__()
For example, you could implement a list iterator like this: ```py
class ListIterator:
def init(self, items):
self.items = items
self.position = 0
def __iter__(self):
return self
def __next__(self):
if self.position >= len(self.items):
self.items = []
raise StopIteration # no more items
item = self.items[self.position]
self.position += 1
return item
numbers_iterated = ListIterator(numbers) # numbers_iterated is an iterator
(this is essentially what iter(a_list) does)
It runs until the code hits a yield. Then the value that's yielded becomes the result of the next() call.
so yes
A generator is just the name for the thing that's produced by a yield-containing function
From an outside perspective, it is just an iterator
something like that, yes
A generator is an iterator. Iterators just produce a potentially endless stream of values. Generators are iterators with extra capabilities.
It is a bit more complex, because you can also send a value to a generator with the .send() method (and throw an exception into it with .throw())
but if we ignore that, we just needed some word to call the result of a yield-function
and I guess the entire feature
If it contains yield, it's considered a generator function. That's the rule
For example, here's how you can make a generator function that yields no values: ```py
def v1():
if False:
yield 42
def v2():
return
yield 42
def v3():
for mystery in []:
yield 42
assert list(v1()) == []
assert list(v2()) == []
assert list(v3()) == []
so it's not dynamically decided, it's literally just whether one of the statements in the function is yield
It's not really a clone of the function, but it's an object that remembers all the local variables and where in the function it's currently executing
In particular, you can have several generators referencing the same function #1418378203898318969 message
This help channel has been closed. Feel free to create a new post in #1035199133436354600. To maximize your chances of getting a response, check out this guide on asking good questions.