An example which shows the power of decorators when combined with generators.
This recipe allows to generate different kinds of series of numbers by applying a decorator over an infinite integer generator. A processing function and a condition function can be used to specify the rules.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | # Simple series generator with
# generators & decorators.
# Author : Anand B Pillai
# Beginning of recipe
def myfunc(**kwds):
def func(f):
# Condition function
cond = kwds['condition']
# Processing function
proc = kwds['process']
# Number of items
num = kwds['number']
x, l = 0, []
for item in f():
if cond and cond(item):
if proc: item=proc(item)
l.append(item)
x += 1
if x==num:
break
return l
return func
def series(condition=None, process=None, number=10):
""" Infinite integer generator """
@myfunc(condition=condition,process=process,number=number)
def wrapper():
x = 1
while 1:
yield x
x += 1
return wrapper
# End of recipe
-----snip-------------snip-------------------------
Examples.
def prime(x):
is_prime=True
for y in range(2,int(pow(x,0.5)) + 1):
if x % y==0:
is_prime=False
break
return is_prime
# Print first 10 prime numbers
print series(condition=prime, process=None, number=10)
[1, 2, 3, 5, 7, 11, 13, 17, 19, 23]
# Print first 10 odd numbers
print series(condition=lambda x: x%2, process=None, number=10)
[1, 3, 5, 7, 9, 11, 13, 15, 17, 19]
# Print squares of first 10 numbers
print series(condition=lambda x: x, process=lambda x: x*x, number=10)
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
# Print a few random numbers
import random
print series(condition=lambda x: x, process=lambda x: random.random(), number=10)
|
This is a convenient way of extracting a required series with a required size out of a generator that returns an infinite stream of numbers.
Since we are using a generator instead of a list comprehension or FP tools such as map/filter, memory is saved for large series. Also custom processing can be done on the items of the series which allows you to write all kinds of series using this recipe.
The generator can also be modified to do some processing of its own, something which it does not do currently.
Generator expressions offer better flexibility, speed, and economy of expression. Since the condition, process, and counter do not share a namespace, lambdas have to be used to specify the expression variable. This is slow, verbose, and clumsy compared to equivalent generator expressions.
If itertools.count is used to generate the series inputs, you get the additional advantage of being able to specify the starting count (from zero, from one, or anywhere else).
Also, itertools.islice() is a flexibile tool for extracting the first n elements of a series. However, more versatility can come from a design that allows an infinite series to be generated simply by dropping the enclosing islice().