Scratch pad nuttiness... (re generators)
So per the blog post below, I'm explaining the Python generator concept in terms of "kicking the can down the road" e.g. getting the next prime in some "just in time" trial by division regimen, per archived examples. http://worldgame.blogspot.com/2009/01/more-geometric-studies.html So then it occurs to me, would there by a way to do a kind of special names overloading such that __next__ (that which triggers the next cycling to yield) might be replaced with the word kick in some namespace, as in kick(o) instead of next(o) -- using Python 3.x syntax here i.e. not o.next(). What would a student try, if this were the challenge? Simple solution:
def kick(o): return next(o)
def f(): # could use a generator expression for i in range(10): yield i
type(f) # just a function so far <class 'function'> o = f() next(o) 0 kick(o) 1 kick(o) 2 type(o) # a generator once instanced <class 'generator'>
Another student might think different and try something like this:
o.__next__ <method-wrapper '__next__' of generator object at 0x8367edc> o.kick = o.__next__ Traceback (most recent call last): File "<pyshell#51>", line 1, in <module> o.kick = o.__next__ AttributeError: 'generator' object has no attribute 'kick'
"Dang, you can't give your generator object a random attribute it doesn't already have, write-protected in some way...." Note that the generator type is inappropriate as a base class for your own kind of user class.
class newgen (type(o)): kick = type(o).__next__
Traceback (most recent call last): File "<pyshell#10>", line 1, in <module> class newgen (type(o)): TypeError: type 'generator' is not an acceptable base type Oh well.... Kirby
Hi Kirby, I happen to be working through some Python3.0 issues right now. A couple comments: On Monday 16 March 2009 10:42:24 kirby urner wrote:
So then it occurs to me, would there by a way to do a kind of special names overloading such that __next__ (that which triggers the next cycling to yield) might be replaced with the word kick in some namespace, as in kick(o) instead of next(o) -- using Python 3.x syntax here i.e. not o.next().
What would a student try, if this were the challenge?
Simple solution:
def kick(o):
return next(o)
Of couse, the simplest solution is just to bind kick to next:
o = iter(range(10)) kick = next kick(o) 0 kick(o) 1
That does the trick.
def f(): # could use a generator expression
for i in range(10): yield i
type(f) # just a function so far
<class 'function'>
o = f() next(o)
0
kick(o)
1
kick(o)
2
type(o) # a generator once instanced
<class 'generator'>
Another student might think different and try something like this:
o.__next__
<method-wrapper '__next__' of generator object at 0x8367edc>
o.kick = o.__next__
Traceback (most recent call last): File "<pyshell#51>", line 1, in <module> o.kick = o.__next__ AttributeError: 'generator' object has no attribute 'kick'
"Dang, you can't give your generator object a random attribute i doesn't already have, write-protected in some way...."
Of course, even if this worked, it wouldn't give you the kick(o) behavior that you specified above. I could see where one might try this to get the 2.xish o.kick() behavior. Of course, you could easily get an alias to o's __next__ via simple assignment again:
o = iter(range(10)) kicko = o.__next__ kicko() 0 kicko() 1
Note that the generator type is inappropriate as a base class for your own kind of user class.
class newgen (type(o)):
kick = type(o).__next__
Traceback (most recent call last): File "<pyshell#10>", line 1, in <module> class newgen (type(o)): TypeError: type 'generator' is not an acceptable base type
I don't follow the reasoning in this attempt at all. --John
On Mon, Mar 16, 2009 at 9:38 AM, John Zelle <john.zelle@wartburg.edu> wrote:
= Kirby = John = Kirby
Simple solution:
def kick(o):
return next(o)
Of couse, the simplest solution is just to bind kick to next:
o = iter(range(10)) kick = next kick(o) 0 kick(o) 1
That does the trick.
Yeah, that's a good one. A naive student would think range is already an iter, "so why are we making an iter and iter?" is what they'll be thinking. More important, you're showing that 'next' is in the toplevel namespace in Python 3.x, and available for reassignment or "shared assignment" (as kick becomes a synonym for next, not a replacement).
Another student might think different and try something like this:
o.__next__
<method-wrapper '__next__' of generator object at 0x8367edc>
o.kick = o.__next__
Traceback (most recent call last): File "<pyshell#51>", line 1, in <module> o.kick = o.__next__ AttributeError: 'generator' object has no attribute 'kick'
"Dang, you can't give your generator object a random attribute i doesn't already have, write-protected in some way...."
Of course, even if this worked, it wouldn't give you the kick(o) behavior that you specified above. I could see where one might try this to get the 2.xish o.kick() behavior. Of course, you could easily get an alias to o's __next__ via simple assignment again:
Yes, the student would then have o.kick() seeming to work just as __next__ does, then still have to snap out of the 2.x trance and think next is something you have to "write home about" (add to the toplevel namespace). In 2.5: IDLE 1.2.2
help(next)
Traceback (most recent call last): File "<pyshell#0>", line 1, in <module> help(next) NameError: name 'next' is not defined In 3.0:
help(next) Help on built-in function next in module builtins:
next(...) next(iterator[, default]) Return the next item from the iterator. If default is given and the iterator is exhausted, it is returned instead of raising StopIteration.
In retrospect, I'd say a lesson for all students is: consult builtin docs a lot right in the shell, get in that habit, because then you'll hold yourself to the same habits, i.e. if you module is not help-aware, then you're cheating your readers, and actually yourself, your chief reader perhaps... Python is generous out of the box, is a good role model of developer-friendly (not the same as newbie friendly, but then that's the job of developers (to welcome apprentice snake charmers into the fold with open arms -- something the Python community is already known for (its welcoming aspect))).
class newgen (type(o)):
kick = type(o).__next__
Traceback (most recent call last): File "<pyshell#10>", line 1, in <module> class newgen (type(o)): TypeError: type 'generator' is not an acceptable base type
I don't follow the reasoning in this attempt at all.
--John
This is a student thinking "oh, if a primitive thingy doesn't do what I want it to, I should subclass it and add my new method". So here said student is trying to subclass the "generator type" by using type(some_class) as a way of saying "what to inherit from" and then trying the same trick as before, except reaching into the superclass for whatever that __next__ thingy is. Kirby
Writing for the Linux Gazette, Pramode C.E. shows us a nifty set of generators for sieving out composites, adapted for Python 3 in the source code below. Is this really a sieve or trial-by-division? http://linuxgazette.net/100/pramode.html def firstn(g, n): for i in range(n): yield next(g) def intsfrom(i): while True: yield i i = i + 1 def exclude_multiples(n, ints): for i in ints: if (i % n): yield i def sieve(ints): while True: prime = next(ints) yield prime ints = exclude_multiples(prime, ints) if __name__ == '__main__': for i in firstn(sieve(intsfrom(2)), 400): print(i)
participants (2)
-
John Zelle -
kirby urner