Chris Angelico wrote:
> On Mon, Mar 16, 2015 at 7:36 PM, Steven D'Aprano> <stev...@pearwood.info> wrote:>> I expect that the interpreter can tell the difference between>>>> yield spam>>>> and>>>> x = yield spam>>>> and only allow send(), close() and throw() on generators which include>> the second form. If it can't, then that's a limitation, not a feature to>> be emulated.>>> > Hmm. That would imply that an expression is different if it's used> somewhere. In Python, there's no difference between these two function> calls:> > x = func(1,2,3)> func(1,2,3)> > except for the actual name binding.
That's a pretty big difference though :-)
Just for the record, it's not just name bindings that make a generator
behave as a coroutine. E.g.:
py> def co():
... print( (yield 1) + 2 )
...
py> a = co()
py> next(a)
1
py> a.send(98)
100
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
Not the most *useful* example, I grant, but it demonstrates the idea.
However, for simplicity let's ignore that and pretend it's only name
binding we care about.
> If a yield expression enabled > send() if and only if its return value were used, then it would be> extremely surprising:
It's surprising to me that you can send() into a non-coroutine and have it
ignored. I'm not even sure that is documented behaviour or just an accident
of implementation.
> def use_and_discard():> while True:> _ = (yield 1)> > def just_yield():> while True:> yield 1> > So... I can send into the first but not into the second?
That's the idea.
The second one is not a coroutine, and cannot be used as a coroutine in any
useful way. I maintain that it is a mistake that you can send() into it at
all, and if somebody does, it is invariably an error. That would be like
send()ing into None, or a str. We know what the Zen says about errors...
> That said, though, it would be quite reasonable for a *linter* to warn> you about sending into a generator that never uses sent values. With a> good type inference system (not sure if MyPy is sufficient here), it> would be possible to distinguish between those two functions and> declare that the first one has the return type "sendable_generator"> and the second one "nonsendable_generator", and give a warning if you> send into the latter. But I don't think that's the language's job.
I do :-)
I think it would be hard for a linter to do this. It can't just do a
type-check, because the types of generator-iterators and
generator-coroutines are the same. It would need to actually analyse the
source code, AST, or byte-code. That's doable, but the compiler already
does that, and compiles different code for the two cases:
py> from dis import dis
py> def g1():
... yield 1
...
py> def g2():
... x = yield 1
...
py> dis(g1)
2 0 LOAD_CONST 1 (1)
3 YIELD_VALUE
4 POP_TOP
5 LOAD_CONST 0 (None)
8 RETURN_VALUE
py> dis(g2)
2 0 LOAD_CONST 1 (1)
3 YIELD_VALUE
4 STORE_FAST 0 (x)
7 LOAD_CONST 0 (None)
10 RETURN_VALUE
so it should be easy for the compiler to recognise a yield used as if it
were a statement versus one used as an expression, and take appropriate
steps. But maybe there are subtle corner cases I haven't thought of.
--
Steven
--
https://mail.python.org/mailman/listinfo/python-list