I read the python docs and learned Iterator, Generator, Decorator, etc., so I wrote FizzBuzz with these for practice.
http://docs.python.jp/2/tutorial/classes.html#iterator
(Begin quote)
These access methods are clear, concise, and convenient. The use of iterators is widespread throughout Python and brings unity. Behind the scenes, the for
statement calls the container object ʻiter (). This function returns an iterator object with the
next ()method defined. The
next ()method accesses the elements in the container one at a time. When there are no more elements in the container to access,
next ()throws a
StopIterationexception and terminates the for loop. The following example shows how it actually works. (Omitted) Once you've seen the mechanism behind the iterator protocol, it's easy to add iterator behavior to your class. Define a
iter ()method to return an object with a
next ()method. If the class itself defines
next (),
iter ()can simply return
self`.
(End of citation)
If you want to make it easy, you can define a class with __iter__ ()
method and next ()
method, and throw an exception of StopIteration
when the termination condition is satisfied.
Iterator instances seem to be usable as-is in for
statements.
I wrote a FizzBuzzIterator with similar specifications by referring to the range () function.
def fizzbuzz(n):
n = int(n)
if (n%15) == 0: return "FizzBuzz"
elif (n%5) == 0: return "Buzz"
elif (n%3) == 0: return "Fizz"
else: return n
class FizzBuzzIterator:
def __init__(self,start, stop, step=1):
self.count = start
self.stop = stop
self.step = step
def __iter__(self):
return self
def next(self):
if (self.step > 0 and self.count >= self.stop) or (self.step < 0 and self.count <= self.stop):
raise StopIteration
else:
result = fizzbuzz(self.count)
self.count += self.step
return result
Output FizzBuzz from 1 to 100
fbiter1 = FizzBuzzIterator(1,101)
for fzbz in fbiter1: print fzbz
Output FizzBuzz only for odd numbers from 1 to 99
fbiter2 = FizzBuzzIterator(1,101,2)
for fzbz in fbiter2: print fzbz
Output FizzBuzz in reverse order from 100 to 1
fbiter3 = FizzBuzzIterator(100,0,-1)
for fzbz in fbiter3: print fzbz
http://docs.python.jp/2/tutorial/classes.html#generator (Begin quote) Generators are a concise and powerful tool for creating iterators. Generators are written like regular functions, but use yield statements to return any data. Each time next () is called, the generator resumes previously interrupted processing (the generator remembers all data values and which statement was last executed). If you look at the example below, you can see that the generator is very easy to create. (End of citation)
Iterator seemed to work very hard, but should the generator just be a for document? It made me wonder.
def fizzbuzz(n):
n = int(n)
if (n%15) == 0: return "FizzBuzz"
elif (n%5) == 0: return "Buzz"
elif (n%3) == 0: return "Fizz"
else: return n
def fizzbuzz_generator(start,finish,diff=1):
for n in range(start,finish,diff):
yield fizzbuzz(n)
for fzbz in fizzbuzz_generator(1,100):
print fzbz
http://docs.python.jp/2/glossary.html#term-decorator
(Begin quote)
(Decorator) A function that returns a function. It is usually used to convert a function with the grammar @wrapper
. Common uses for decorators are classmethod ()
and staticmethod ()
.
The decorator grammar is syntactic sugar. The following two function definitions are semantically identical.
def f(...):
...
f = staticmethod(f)
@staticmethod
def f(...):
...
(End of citation)
I didn't understand so I wrote it.
def fizzbuzz(n):
n = int(n)
if (n%15) == 0: return "FizzBuzz"
elif (n%5) == 0: return "Buzz"
elif (n%3) == 0: return "Fizz"
else: return n
def fizzbuzz_decorator(func):
def wrapper(*args, **kwargs):
func(fizzbuzz(*args))
return wrapper
@fizzbuzz_decorator
def f(n):
print n
f(1)
f(2)
f(3)
f(4)
f(5)
In the above, it seems that f is rewritten by the decorator expression @fizzbuzz_decorator
, and f is rewritten tofizzbuzz_decorator (f)
, that is, the following wrapper () function.
def wrapper(*args, **kwargs):
f(fizzbuzz(args[0]))
So, since the argument of f is processed by the fizzbuzz () function and then passed to the original f, it seems that the fizzbuzz string will be output.
Execution result
1
2
Fizz
3
Buzz
It seems that decorator expressions can also be nested. http://docs.python.jp/2/reference/compound_stmts.html#function (Begin quote) Function definitions can be wrapped in one or more decorator expressions. When you define a function, the decorator expression is evaluated in the scope that contains the function definition. The result must be a callable object that takes a function object as its only argument. Instead of a function object, the returned value is bound to the function name. Multiple decorators are nested and applied. (End of citation)
def fizzbuzz(n):
n = int(n)
if (n%15) == 0: return "FizzBuzz"
elif (n%5) == 0: return "Buzz"
elif (n%3) == 0: return "Fizz"
else: return n
def fizzbuzz_decorator(func):
def wrapper(*args, **kwargs):
func(fizzbuzz(*args))
return wrapper
def fizzbuzz_decorator2(func):
def wrapper(*args, **kwargs):
for n in range(1,args[0]):
func(n)
return wrapper
@fizzbuzz_decorator2
@fizzbuzz_decorator
def f(n):
print n
f(101)
This is a pattern equivalent to the following.
f = fizzbuzz_decorator2(fizzbuzz_decorator(f))
Execution result
1
2
Fizz
3
Buzz
(Omitted)
98
Fizz
Buzz
It seems that you can pass arguments to the decorator, so I will add it as soon as I investigate.
Recommended Posts