constructive criticism sought (Rob Andrews)
Hello Rob First, as an erstwhile 'consumer' of Useless Python, thanks for the work you and now your volunteer team have done on that site. It's good fun to surf. Second, by way of offering (hopefully) constructive criticism, kindly accept the following points: 1. PLEASE remove it from yahoo! groups. I would be quite happy to volunteer but was unhappy with going via yahoo to do so. 2. Rather than toss truly useless code (I think my few contributions have probably landed in that bin!!), would it be possible to catalogue it and remove duplicates. I suspect that many snippets of code are those done by newbies (such as myself) who are working through tuts or text books, etc, so is likely to be replicated. 3. With the foregoing in mind however, if it is to be "Useless" Python, then perhaps it could be graded because there is some pretty complex material on the site last time I looked and then there is some pretty basic stuff too. It would be a shame, I think, if the really basic stuff was elided by the complex, not for any other reason than the sense of accomplishment a newbie might get surfing through and reading code that s/he can actually understand and even contribute to without feeling daunted/intimidated by really elaborate/complex code. Let's face it, a certain % of that code is never going to be worth anything, which surely is as good a definition of useless as anything I've ever come across. Third, look forward to seeing the new site once it is done and dusted. Well done Rob - it was a good idea, and I for one am happy that you continued with it. Thanks. Andrew
I thanked people privately for their responses to my question, but I wanted to take a moment to say "thanks" to the list generally. "Thanks." -Rob Andrews
-----Original Message----- Hello Rob
First, as an erstwhile 'consumer' of Useless Python, thanks for the work you and now your volunteer team have done on that site. It's good fun to surf.
Second, by way of offering (hopefully) constructive criticism, kindly accept the following points:
The construct: for i in collection: do something with i is extremely common. The collection is most simply a list or tuple, but may in fact be any iterable object. So the construct might be rewritten: for i in iterable: # construct (1) do something with i But how do we ensure that this is not an infinite loop? I.e. the collection returned by iterable may be inherently open-ended, e.g. the sequence of fibonacci numbers 1 1 2 3 5 8 13... A common modification to deal with such cases is: for i in iterable: # construct (2) if test(i): break do something with i An alternative to the above is to return to the original construct (1), but have the iterable become self-terminating. The syntax: iterable = iter(obj,sentinel) uses the builtin iter() to compare a callable to a sentinel, and stops iterating when they match. So lets define an object that returns the next fibonacci number whenever called, and which internalizes a test, which, if positive, will return the sentinel value: ---- # iters.py class Fib(object): def __init__(self,start=0,next=1,test=False,sentinel=-1): self.start = start self.next = next self.sentinel = sentinel self.test = test def __call__(self): self.start,self.next = self.next, self.start + self.next if self.test(self.start): return self.sentinel return self.start def t(x): """x > 1000""" return x > 1000 fibobj = Fib(test=t) iterable = iter(fibobj,-1) for i in iterable: print i, ---- If you run the above script you'll get a listing of Fibonacci numbers less than or equal to 1000: Sun 03/30/2003 11:57:21.79 D:\Program Files\Python22>python ./work/iters.py 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 Why go to all the trouble of passing in a test function, if we can simply set a sentinel value to abort iteration? The Fibonacci example suggests the answer: we don't know in advance exactly what the collection will be (or think of prime numbers), so we're not in a position to use a simple value to abort iteration -- we need something like a comparison, or a more complicated set of criteria, which is why we pass in a test. A callable object, turned into an iterator by way of iter(), behaves much the same as a generator object -- which objects may be functions. The Fib object might be rewritten as a generator, still accepting a test function as a parameter:
def fibfunc(test): data = [1,1] while not test(data[0]): yield data[0] data[0],data[1] = data[1],data[0]+data[1] return
iterable = fibfunc(t) # t is a test function for i in iterable: print i,
1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 In this case, fibfunc automatically returns an iterable. What is the advantage of passing a test into an iterable, versus simply going with construct (2)? From a readability point of view, this approach has the advantage of binding the test to the iterable at the time of definition. The same for-loop might handle lots of iterators, and needn't worry about which test to use. The test logically belongs with the iterator. For example, let's modify iters.py (keeping original Fib object intact): ---- def testfunc(max): """a factory for test functions""" def t(x): return x > max t.__doc__ = "stop when x > %s" % max return t t1 = testfunc(1000) t2 = testfunc(5000) def looper(iterable): # generic loop, doesn't worry about when to break for i in iterable: print i, print print t1.__doc__ looper(iter(Fib(test=t1),-1)) print t2.__doc__ looper(iter(Fib(test=t2),-1)) ----
reload(iters) stop when x > 1000 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 stop when x > 5000 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 <module 'iters' from 'D:\Program Files\Python22\work\iters.py'>
In this case, the same test functions will work with the generator version. Example:
iterable = fibfunc(t2) for i in iterable: print i,
1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 --- Kirby
participants (3)
-
ahimsa -
Kirby Urner -
Rob Andrews