Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Yield after the return in Python function.

30 views
Skip to first unread message

Bischoop

unread,
Apr 5, 2021, 8:26:10 AM4/5/21
to
The return suspends the function execution so how is it that in below
example I got output: <generator object doit at 0x7f57fd2912e0>

def doit():
return 0
yield 0

print(doit())



Frank Millman

unread,
Apr 5, 2021, 8:38:41 AM4/5/21
to
The 'yield' in the function makes the function a 'generator' function.

'Calling' a generator function does not execute the function, it returns
a generator object.

You have to iterate over the generator object (e.g. by calling next() on
it) in order to execute the function and return values.

Frank Millman



Frank Millman

unread,
Apr 5, 2021, 8:38:46 AM4/5/21
to
On 2021-04-05 2:25 PM, Bischoop wrote:

Terry Reedy

unread,
Apr 5, 2021, 1:45:33 PM4/5/21
to
*Any* use of 'yield' in a function makes the function a generator
function. This is a simple rule that any person, and just as important,
any automated algorithm, can understand. If there were a 'dead
(unreachable) code' exception, a reader or compiler would have to
analyze each use of 'yield' and decide whether it is reachable or not.
And we would have to decide whether just 1 or all 'yield's had to be
reachable.

In the following code, in 3.x, it is also clear that 'yield' is unreachable.

>>> def f():
if False:
yield 0
return 1

>>> f()
<generator object f at 0x0000023A7F3B0CF0>

But 'False' could be a more complex and less obvious but still
equivalent expression, such and 'a and .... and not a'*. Is 'log(a) =
0' tautologically False?

*While 'a and not a' == False in logic, in Python it might raise
NameError. But that would still mean that it is never True, making
'yield 0' still unreachable.

--
Terry Jan Reedy

Chris Angelico

unread,
Apr 5, 2021, 1:54:23 PM4/5/21
to
On Tue, Apr 6, 2021 at 3:46 AM Terry Reedy <tjr...@udel.edu> wrote:
> *While 'a and not a' == False in logic, in Python it might raise
> NameError. But that would still mean that it is never True, making
> 'yield 0' still unreachable.
>

And even just the lookup can have side effects, if your code is
pathologically stupid.

>>> class Wat(dict):
... def __missing__(self, key):
... global count
... count -= 1
... return count
...
>>> count = 2
>>> eval("print(a and not a)", Wat(print=print))
True

So Python can't afford to treat this as dead code.

ChrisA

Terry Reedy

unread,
Apr 5, 2021, 3:13:00 PM4/5/21
to
On 4/5/2021 1:53 PM, Chris Angelico wrote:
> On Tue, Apr 6, 2021 at 3:46 AM Terry Reedy <tjr...@udel.edu> wrote:
>> *While 'a and not a' == False in logic, in Python it might raise
>> NameError. But that would still mean that it is never True, making
>> 'yield 0' still unreachable.

When I wrote that, I knew I might be missing something else.

> And even just the lookup can have side effects, if your code is
> pathologically stupid.

Or pathologically clever.

>>>> class Wat(dict):
> ... def __missing__(self, key):
> ... global count
> ... count -= 1
> ... return count

'__missing__' is new since I learned Python. I barely took note of its
addition and have never used it. Thanks for the example of what it can
do. One could also make it randomly return True or False.

>>>> count = 2
>>>> eval("print(a and not a)", Wat(print=print))
> True
>
> So Python can't afford to treat this as dead code.

This gets to the point that logic and math are usually atemporal or at
least static (as in a frozen snapshot), while computing is dynamic. In
algebra, the canon is that all instances of a variable are replaced by
the same value.

Python *could* do the same for expresssions: load 'a' (in this case)
once into a register or stack slot and use that value consistently
throughout the expression. Replacing the eval with the following exec
has the same effect.

exec("tem=a; print(tem and not tem)", Wat(print=print))
# print False

In this example, one could disable the binding with __setitem__
(resulting in printing 0), but python code cannot disable internal
register or stack assignments.

--
Terry Jan Reedy

Chris Angelico

unread,
Apr 5, 2021, 3:32:53 PM4/5/21
to
On Tue, Apr 6, 2021 at 5:14 AM Terry Reedy <tjr...@udel.edu> wrote:
>
> On 4/5/2021 1:53 PM, Chris Angelico wrote:
> > On Tue, Apr 6, 2021 at 3:46 AM Terry Reedy <tjr...@udel.edu> wrote:
> >> *While 'a and not a' == False in logic, in Python it might raise
> >> NameError. But that would still mean that it is never True, making
> >> 'yield 0' still unreachable.
>
> When I wrote that, I knew I might be missing something else.
>
> > And even just the lookup can have side effects, if your code is
> > pathologically stupid.
>
> Or pathologically clever.
>
> >>>> class Wat(dict):
> > ... def __missing__(self, key):
> > ... global count
> > ... count -= 1
> > ... return count
>
> '__missing__' is new since I learned Python. I barely took note of its
> addition and have never used it. Thanks for the example of what it can
> do. One could also make it randomly return True or False.

Yep. It could be done with a __getitem__ method instead; the point is
that simply looking up a simple name can have side effects and/or be
nondeterministic.

> >>>> count = 2
> >>>> eval("print(a and not a)", Wat(print=print))
> > True
> >
> > So Python can't afford to treat this as dead code.
>
> This gets to the point that logic and math are usually atemporal or at
> least static (as in a frozen snapshot), while computing is dynamic. In
> algebra, the canon is that all instances of a variable are replaced by
> the same value.

Right - or rather, that in algebra, a "variable" is really a
placeholder for a single, specific value, which may perhaps be unknown
to us, but which has a very well-defined value.

(At least, that's the case with most values, and with a convergent
series. Things can break down a bit with a divergent series, but even
then, analytic continuation can sometimes give you a well-defined
value.)

> Python *could* do the same for expresssions: load 'a' (in this case)
> once into a register or stack slot and use that value consistently
> throughout the expression. Replacing the eval with the following exec
> has the same effect.

True, but I think that this would be enough of a semantic change that
Python should be very VERY careful about doing it. A *programmer* can
choose to do this (and we see it sometimes as an optimization, since
global lookups can be a lot more costly), but the interpreter
shouldn't.

> exec("tem=a; print(tem and not tem)", Wat(print=print))
> # print False
>
> In this example, one could disable the binding with __setitem__
> (resulting in printing 0), but python code cannot disable internal
> register or stack assignments.
>

Indeed. That said, though, I think that any namespace in which
referencing the same simple name more than once produces this sort of
bizarre behaviour should be considered, well, unusual. NORMAL code
won't have to concern itself with this. But the language spec doesn't
require us to write normal code......

ChrisA

Avi Gross

unread,
Apr 5, 2021, 7:22:30 PM4/5/21
to
Terry: ... '__missing__' is new since I learned Python ...

With so many new dunder variables added, I am wondering when some dunderhead
comes up with:

__mifflin__

The documented use paper is:

https://theoffice.fandom.com/wiki/Dunder_Mifflin_Paper_Company



-----Original Message-----
From: Python-list <python-list-bounces+avigross=veriz...@python.org> On
Behalf Of Terry Reedy
Sent: Monday, April 5, 2021 3:01 PM
To: pytho...@python.org
Subject: Re: Yield after the return in Python function.

On 4/5/2021 1:53 PM, Chris Angelico wrote:
> On Tue, Apr 6, 2021 at 3:46 AM Terry Reedy <tjr...@udel.edu> wrote:
>> *While 'a and not a' == False in logic, in Python it might raise
>> NameError. But that would still mean that it is never True, making
>> 'yield 0' still unreachable.

When I wrote that, I knew I might be missing something else.

> And even just the lookup can have side effects, if your code is
> pathologically stupid.

Or pathologically clever.

>>>> class Wat(dict):
> ... def __missing__(self, key):
> ... global count
> ... count -= 1
> ... return count

'__missing__' is new since I learned Python. I barely took note of its
addition and have never used it. Thanks for the example of what it can do.
One could also make it randomly return True or False.

>>>> count = 2
>>>> eval("print(a and not a)", Wat(print=print))
> True
>
> So Python can't afford to treat this as dead code.

This gets to the point that logic and math are usually atemporal or at least
static (as in a frozen snapshot), while computing is dynamic. In algebra,
the canon is that all instances of a variable are replaced by the same
value.

Python *could* do the same for expresssions: load 'a' (in this case) once
into a register or stack slot and use that value consistently throughout the
expression. Replacing the eval with the following exec has the same effect.

exec("tem=a; print(tem and not tem)", Wat(print=print)) # print False

In this example, one could disable the binding with __setitem__ (resulting
in printing 0), but python code cannot disable internal register or stack
assignments.

--
Terry Jan Reedy

--
https://mail.python.org/mailman/listinfo/python-list

Greg Ewing

unread,
Apr 5, 2021, 8:02:26 PM4/5/21
to
On 6/04/21 4:02 am, Terry Reedy wrote:
> *Any* use of 'yield' in a function makes the function a generator
> function.  ...  If there were a 'dead
> (unreachable) code' exception, a reader or compiler would have to
> analyze each use of 'yield' and decide whether it is reachable or not.

It would also break existing code. An unreachable "yield" is
sometimes used as a way to get a generator that doesn't yield
anything.

--
Greg

Dan Stromberg

unread,
Apr 5, 2021, 10:36:35 PM4/5/21
to
On Mon, Apr 5, 2021 at 10:46 AM Terry Reedy <tjr...@udel.edu> wrote:

> If there were a 'dead
> (unreachable) code' exception, a reader or compiler would have to
> analyze each use of 'yield' and decide whether it is reachable or not.
>

It's also subject to how hard the compiler feels like trying in any given
release. Many compilers have simple unreachable code warnings.

But to do a 100% accurate job of yield detection would be equivalent to the
halting problem, which is a classic problem in computer science that cannot
be solved 100% accurately in finite time.

Terry Reedy

unread,
Apr 5, 2021, 10:39:43 PM4/5/21
to
On 4/5/2021 3:32 PM, Chris Angelico wrote:

> On Tue, Apr 6, 2021 at 5:14 AM Terry Reedy <tjr...@udel.edu> wrote:
>> Python *could* do the same for expresssions: load 'a' (in this case)
>> once into a register or stack slot and use that value consistently
>> throughout the expression. Replacing the eval with the following exec
>> has the same effect.
>
> True, but I think that this would be enough of a semantic change that
> Python should be very VERY careful about doing it.

I consider it beyond a possibility.

> A *programmer* can
> choose to do this (and we see it sometimes as an optimization, since
> global lookups can be a lot more costly), but the interpreter
> shouldn't.

Agreed. My interest is in elucidating the different between math and
computing. The reason some prohibit rebinding bound names within a
local context is to make code more like math. But this example should
that Python code can effectively rebind names 'invisibly'.

Side note: Since a loop is equivalent to a recursive tail call, I think
rebinding once per loop should be considered to be consistent with
no-rebinding. When an compiler or interpreter re-writes tail recursion
as while looping, the result is the same. I don't believe in forcing
people to take the detour of using recursion syntax, which for many is
harder to write and understand.


>> exec("tem=a; print(tem and not tem)", Wat(print=print))
>> # print False
>>
>> In this example, one could disable the binding with __setitem__
>> (resulting in printing 0), but python code cannot disable internal
>> register or stack assignments.

> Indeed. That said, though, I think that any namespace in which
> referencing the same simple name more than once produces this sort of
> bizarre behaviour should be considered, well, unusual. NORMAL code
> won't have to concern itself with this. But the language spec doesn't
> require us to write normal code......

I believe in the freedom to write 'strange code'. But my other interest
is how to talk about Python and 'normal' code.

--
Terry Jan Reedy

Chris Angelico

unread,
Apr 7, 2021, 7:28:49 AM4/7/21
to
On Tue, Apr 6, 2021 at 12:40 PM Terry Reedy <tjr...@udel.edu> wrote:
>
> On 4/5/2021 3:32 PM, Chris Angelico wrote:
>
> > On Tue, Apr 6, 2021 at 5:14 AM Terry Reedy <tjr...@udel.edu> wrote:
> >> Python *could* do the same for expresssions: load 'a' (in this case)
> >> once into a register or stack slot and use that value consistently
> >> throughout the expression. Replacing the eval with the following exec
> >> has the same effect.
> >
> > True, but I think that this would be enough of a semantic change that
> > Python should be very VERY careful about doing it.
>
> I consider it beyond a possibility.
>

I just realised that the whole eval/exec/namespace stuff is massive
overkill. All you need is an object that is inconsistent in its
boolification...

>>> class Wat:
... def __bool__(self):
... self.state = not getattr(self, "state", False)
... return self.state
...
>>> a = Wat()
>>> a and not a
True

ChrisA

Stestagg

unread,
Apr 7, 2021, 8:30:26 AM4/7/21
to
On Wed, Apr 7, 2021 at 12:31 PM Chris Angelico <ros...@gmail.com> wrote:

>
> I just realised that the whole eval/exec/namespace stuff is massive
> overkill. All you need is an object that is inconsistent in its
> boolification...
>
>
Somewhat related: https://bugs.python.org/issue42899

Steve

Chris Angelico

unread,
Apr 7, 2021, 8:42:36 AM4/7/21
to
Yup. There are a very few edge cases where pathological behaviour can
be optimized out. Sometimes, there's an alternative way to describe it
(for example, containment is defined as "identity or equality"), but
other times, you can't actually pin down the exact semantics in Python
code. Or maybe it's possible, but really hard; the precise meaning of
"yield from iterable" is quite the read - check out PEP 380, and
notice that even just calling a method isn't as simple as it looks :)

ChrisA
0 new messages