Lecture Notes 18#
timer as context manager#
import time
class Timer:
def __enter__(self):
self.t1 = time.time()
def __exit__(self, *args):
self.t2 = time.time()
print(f"Time used here: {self.t2 - self.t1}")
with Timer() as t:
print("Hello")
time.sleep(1)
print("World")
Hello
World
Time used here: 1.000225305557251
Iterables#
an object that supports iteration
allowed in the top a for loop
iter([])
<list_iterator at 0x7f085188df00>
iter([...])
delegates to[...].__iter__()
'__iter__' in dir([])
True
not all objectes are iterable
iter(1)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Cell In[6], line 1
----> 1 iter(1)
TypeError: 'int' object is not iterable
'__iter__' in dir(int)
False
iterators#
Generate values of a sequence when called by next
'__next__' in dir(iter([])) # True for an iterator
True
next(iter([1, 2, 3]))
1
it = iter([1, 2, 3])
next(it)
1
next(it)
2
next(it)
3
next(it)
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
Cell In[28], line 1
----> 1 next(it)
StopIteration:
for element in [1, 2, 3]:
print(element)
1
2
3
classes supporting iteration#
class Counter:
def __init__(self, size):
print("__init__:", size)
self.size = size
self.start = 0
def __iter__(self):
print("__iter__:", self.size)
return CounterIter(self.start, self.size)
class CounterIter:
def __init__(self, start, size):
self.start = start
self.size = size
def __next__(self):
if self.start < self.size:
self.start = self.start + 1
return self.start
raise StopIteration
for n in Counter(3):
print(n)
__init__: 3
__iter__: 3
1
2
3
a_iterable = Counter(3)
__init__: 3
a_iterator = iter(a_iterable)
__iter__: 3
next(a_iterator)
1
next(a_iterator)
2
next(a_iterator)
3
next(a_iterator)
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
Cell In[66], line 1
----> 1 next(a_iterator)
Cell In[59], line 18, in CounterIter.__next__(self)
16 self.start = self.start + 1
17 return self.start
---> 18 raise StopIteration
StopIteration:
generators#
look like function
has
yield
statement instead ofreturn
def g(n):
print('enter g with',n)
yield n
yield n + 1
print('after yield')
g(2)
<generator object g at 0x7f8e701093c0>
'__iter__' in dir(g(2))
True
'__next__' in dir(g(2))
True
list(g(2))
enter g with 2
after yield
[2, 3]
for n in g(2):
print(n)
enter g with 2
2
3
after yield
Example#
a given number of Fibonacci sequence nubmers
def f(n):
"""
Return the n first numbers in the Fibonacci sequence
"""
count = 0
a = 0
b = 1
while count < n:
yield a
a, b = b, a+b
count += 1
list(f(5))
[0, 1, 1, 2, 3]
list(f(10))
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]