# Background

# Counter, generators, and classes

- Time
- 15 minutes
- Activity
- Demo

Recall our generator based counter. In
Lab 19 we *almost* simulated the generator behaviour with a
closure, except that `next(counter)`

was replaced by `counter()`

. We
can provide a compatible interface by using a python
class.

```
class Counter:
"Simulation of generator using only __next__ and __init__"
def __init__(self,x):
self.x = x
self.first = x
def __next__(self):
self.x = self.x + 1
return self.x - 1
print('first')
counter = Counter(100)
print('second')
print(next(counter))
print('third')
print(next(counter))
print('last')
```

- Observe that the implimentation is closer to the closure based version of L19 than the original generator version.
- It's a reoccuring theme in Python to have some
syntax/builtin-function tied to defining a special
`__foo__`

method

# Fibonacci, again

- Time
- 35 minutes
- Activity
- Small groups

Save the generated based Fibonacci example as `~/fcshome/cs2613/L20/fibgen.py`

Save the following in `~/fcshome/cs2613/L20/fib.py`

```
#!/usr/bin/python3
class Fib:
def __init__(self,max):
self.max = max
self.a = 0
self.b = 1
def __next__(self):
if self.a < self.max:
else:
raise StopIteration
```

Complete the definition of `__next__`

so that following test passes.
*Hint*: Look at your solution for the closure based version from
Lab 19.

```
from fib import Fib
from fibgen import fibgen
def test_fib_list():
genfibs=list(fibgen(100))
fibber=Fib(100)
fibs=[]
while True:
try:
fibs.append(next(fibber))
except:
break
assert genfibs == fibs
```

We may wonder why this odd looking while loop is used to build `fibs`

rather than some kind of list comprehension. The following simpler version currently fails:

```
def test_fib_list_2():
genfibs=list(fibgen(100))
classfibs=list(Fib(100))
assert genfibs==classfibs
```

This failure is due the fact that we haven't really built an iterator yet. Remember Python works by duck-typing, which means that an iterator is something that provides the right methods.

Add the following method to your `Fib`

class. Observe the test above now
passes.

```
def __iter__(self):
return self
```

In addition to signal to the Python runtime that some object is an
iterator, the `__iter__`

serves to restart the traversal of our "virtual list".
If we want iterators to act as lists, then the following should really print the same list of Fibonacci numbers twice

```
if __name__ == '__main__':
fibber = Fib(100)
for n in fibber:
print(n)
for n in fibber:
print(n)
```

Since it doesn't, let's formalize that as a test. Modify the
`__init__`

and (especially) the `__iter__`

method so that following
test passes.

```
def test_fib_restart():
fibobj = Fib(100)
list1 = list(fibobj)
list2 = list(fibobj)
assert list1 == list2
```

# Object copying and equality

- Time
- 25 min
- Activity
- Small groups.

Create a file `~/fcshome/cs2613/labs/L20/expr.py`

defining a class
`Expr`

Write an `__init__`

and
`__eq__`

so that the following test passes. You likely want either the `vars`

builtin function or the `__dict__`

method.

```
from expr import Expr;
from copy import deepcopy;
six_plus_nine = Expr('+', 6, 9);
six_times_nine = Expr('*', 6, 9);
compound1 = Expr('+', six_times_nine, six_plus_nine)
compound2 = Expr('*', six_times_nine, compound1)
compound3 = Expr('+', compound2, 3)
def test_equality():
assert six_plus_nine == deepcopy(six_plus_nine)
assert compound1 == deepcopy(compound1)
assert compound2 == deepcopy(compound2)
assert compound3 == deepcopy(compound3)
```

# Arithmetic

- Time
- 25 min
- Activity
- Small groups.

Add an `eval`

method to your `Expr`

class so that the following
additional test passes.

```
def test_basic():
assert six_plus_nine.eval() == 15
assert six_times_nine.eval() == 54
```

Use deepcopy to update
`test_basic`

to make sure that eval does not modify the object `self`

.

If the following test does not already pass, update your `eval`

method so that it does

```
def test_recur():
assert compound1.eval() == 69
assert compound2.eval() == 3726
assert compound3.eval() == 3729
```

Add similar checking for mutation to `test_recur`