how do I make an empty generator? (intermediate) anthony explains
Вставка
- Опубліковано 30 вер 2024
- today I talk about how (and a little bit why) to make an empty generator in python! the syntax is a little quirky but I show you the details about what makes a generator a generator and why it's tricky to write an empty one!
- generator basics: • generator basics (+typ...
- generator send / return type: • python typing: Generat...
- new generator shorthand! • PEP 696 is a huge qual...
playlist: • anthony explains
==========
twitch: / anthonywritescode
dicsord: / discord
twitter: / codewithanthony
github: github.com/aso...
stream github: github.com/ant...
I won't ask for subscriptions / likes / comments in videos but it really helps the channel. If you have any suggestions or things you'd like to see please comment below!
Just wait until 3.13 introduces the yieldn't keyword
`yield from ()` is indeed pretty elegant. One potential downside of it is that it actually compiles to a bunch of extra bytecode that actually creates the empty tuple and then tries to iterate over it.
On the other hand, `return + yield` and `if False: yield` both compile to what seems to be the minimal number of instructions that emulate the required behavior.
Also, regarding the correct type signature for such a generator, I think it should be
def gen() -> Generator[typing.Never, typing.Any, None]:
return
yield
because the yield type (first position) is covariant, while the send type (second position) is contravariant. So Generator[Never, Any, None] should be the broadest correct annotation. The generator never yields any value, accepts any sent value (because you'll never have an opportunity to actually send anything to it) and return None.
Was just about to comment this as i was playing around disassembling the components :)
Showcasing the fix of a bug you found in a widely used library is cool.
Empty iterables in general are the modulus of most operations, so they're really important. Thanks for the useful idioms.
Expectation: to get my mind blown. Reality: got my mind blown.
Damm, 😬 The more I learn python the more I am able to understand that I didn't learn anything 😢.
I'm gonna win so many bets, now that I know "return yield" is a valid syntax.
no, its return
yield
I feel like maybe we should have def and defg for generator functions, as yield changing the whole function is very surprising
Before watching this video fully I was guessing that
def a():
if False:
yield
is the most efficient way of doing that.
Before return was accepted in generator i usually used `while False: yield` 😅
I actually had a very similar problem, I'll try the return + yield. In my case, I wanted to have an abstract method that returns an AsyncGenerator, so child classes implement it, as an interface. Well, if you define an async function that returns an AsyncGenerator, most abstract functions only 'pass', which was causing mypy to think the return type was Coroutine[AsyncGenerator] instead. Even with explicit type hinting it did not work, I had to make the abstract function not async for mypy to be happy, but that is even worse in my view...
I paused it at 30 seconds before seeing the answer. The way I'd do it is...
def empty_gn():
if False:
yield "never"
... or if you want to fool your IDE about unreachable code do this:
def empty_gn():
if True in [False]:
yield "never"
Now I'll watch the rest and see if that's similar to what you do.
ah... cool. I like "yield from ()" ... more readable.
Interesting that we both mentioned issues around unreachable code in the two different solutions.
Also, I'd argue that mypy was correct that the yield was unreachable.
Granted, my `yield "never"` is also unreachable but the day MyPy or PyCharm start executing code to determine if something is reachable or not is the day I stop using it anyway.
My old trick for this with PyCharm was to use "if 1 == 1", then "if 1 + 1 == 2", but PyCharm started evaluating that stuff. I'm not really happy about that.
return yeild feels wrong and bad lol
My first try was:
for i in range(0,0):
yield i
Works but needs optimization.
Worked great.
2:44 ... booom ... mind blown
It’s super fun cause I had to do them yesterday ahahah nice Anthony
Will you make a 3.13 release highlight video
yep! it just got to rc1 so I'm compiling my thoughts!
what are the usages of empty generator
I’ve always wanted to use a for loop as a spooky noop: for _ in noop(): sys.exit()
It allows for consistency of the type of object you expect: a generator. I just had a practical use case for this: I had a function that yields elements. Each element is kind of expensive to get, and there was a single, cheap check early in the function that, if True, made the function return `None`. That meant I had to check for `None` first when calling the function. Now by returning an empty generator, I can just do `for e in my_function():` regardless, the type returned is a generator.
@@Clepsyd_ thank you for answer. Totally Makes sense.
What is an example use case for using an empty generator?
it's right at the beginning!
@@anthonywritescode I think the question is about a practical example of such usage scenario
I really like how we answered the "How" question but skipped the "WTF why?"
He answers it like 45 seconds into the video
in short, if you want to be able to swap in a null-generator somewhere that only accepts generators you'd need this