[Tutor] How to extract numerator and denominator from fractions.Fraction(4, 32)?
Oscar Benjamin
oscar.j.benjamin at gmail.com
Tue Aug 6 19:56:14 CEST 2013
On 6 August 2013 17:33, Alan Gauld <alan.gauld at btinternet.com> wrote:
> On 06/08/13 10:01, Walter Prins wrote:
>
>> py> def cr(): # Co-Routine.
>> ... x = yield()
>> ... while True:
>> ... x = yield(x + 1)
>> ...
>
>
>> "send" and yield as akin to threaded execution, where cr() is like a
>> thread that blocks at the yield call after creation, and then when you
>> call on magic.send() the "thread" wakes up and continues and eventually
>> returns another value
>
>
> It's clever but I'm not keen on it overloading yield to do it.
> yield as a word conveys (to me at least) the idea of returning a value but
> not quite completely ending. This usage sounds like a different concept and
> I'd have preferred a more explicit name - although I can't think what!!
I thought of Python's yield in the sense that 'to yield' means 'to
concede'. So funcA yields execution to to the parent frame. The new
'yield from' sort of messes that interpretation up though since it
should really be 'yield to' to make sense in this way.
> Also
> I'm not keen on the argument/parameter mechanism
> here either. Arguments are sent but not explicitly declared in the receiver,
> that all feels rather un-pythonic to me.
I'm not really sure what you mean here.
> But I've only
> skimmed it so far, I need to do some hands-on playing I think.
>
> I am getting concerned that python is developing a lot of these
> non-intuitive type features, almost because it can(*). A lot of it no doubt
> scratches somebody's itch but its at the expense of making what was a very
> easy to use language ever more complex. I'm not sure if i would choose
> Python for my Learn to Program tutorial if I was starting it these days -
> although I'm not sure what else is any better...
No one needs to use .send() if they don't want to. It's also so
uncommonly used that you don't need to mention it in any introductory
tutorial.
There's also .throw():
def cr():
try:
yield
except Exception as e:
print('received: %s' % e)
yield
g = cr()
next(g) # Advance to yield
g.throw(ValueError('42'))
Some time ago I wanted to be able to compute statistics over some data
in a single-pass and I wanted to think of an easy way to invert a
function that consumes an iterator so that I could push values in. The
basic idea is to do something like:
mean = Mean()
var = Var()
max = Max()
for x in data:
mean.update(data)
var.update(data)
max.update(data)
print('mean:', mean.compute())
print('var:', var.compute())
print('max:', max.compute())
Then I realised that any function that computes a fold over an
iterator can be inverted using yield so that this
def isum(iterable, start=0):
total = start
for x in iterable:
total += x
return total
becomes this
def gsum(start=0):
total = start
yield lambda: total
while True:
total += yield
gen = gsum()
evaluate = next(gen)
next(gen) # Move to second yield
push = gen.send
push(1)
push(2)
push(3)
print(evaluate()) # 6
It looks a little shaky like this but you can use a decorator to clean it up:
import functools
def gencalc(gfunc):
@functools.wraps(gfunc)
def wrapper(*args, **kwargs):
gen = gfunc(*args, **kwargs)
evaluate = next(gen)
next(gen) # Move to second yield
push = gen.send
return push, evaluate
return wrapper
@gencalc
def gsum(start=0):
total = start
yield lambda: total
while True:
total += yield
push, evaluate = gsum()
Then it's easy to make other generator calculation functions:
@gencalc
def gmax():
yield lambda : currentmax
currentmax = yield
while True:
newval = yield
if newval > currentmax:
currentmax = newval
@gencalc
def gvar(): # I haven't tested that this is correct
yield lambda: totalvar / (count - 1) if count > 1 else 0
mean = 0
totalvar = 0
for count in itertools.count():
newval = yield
oldmean = mean
mean += (newval - mean) / count
totalvar += (newval - oldmean) * (newval - mean)
The obvious way to do this without generators is something like:
class Sum:
def __init__(self, start=0):
self.total = start
def push(self, value):
self.total += value
def evaluate(self):
return self.total
But I prefer the version that keeps it all in one function and still
looks a lot like the normal function that consumes an iterable.
Oscar
More information about the Tutor
mailing list