Partly related but generators clicked for me when I heard them described as, "instead of pushing data into a function, you're pulling results out of a function."
I've been doing a lot of asyncio lately and just having a mental model of my pipelines working by pulling results through them has been invaluable.
But you're also pushing data back in. Generators are coroutines that can be suspended and resumed, and the yield keyword is a mechanism for two way communication between the routine and coroutine.
Shameless plug: I run a website with dozens of such puzzles that can be solved online. If you're interested, google "zebra puzzles", should be the first link.
I've been doing a lot of asyncio lately and just having a mental model of my pipelines working by pulling results through them has been invaluable.