Chris Angelico wrote: > On Mon, Mar 16, 2015 at 7:36 PM, Steven D'Aprano > <steve+comp.lang.pyt...@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