Hack Bulgaria
github.com/HackBulgaria
Object of any class that defines __iter__ is called an iterable. Iterable is capable of returning only one of it's members at a time.
How can we use this?
# for-loop pseudo code
iterator_obj = iter(iterable)
while True:
try:
element = next(iterator_obj)
except StopIteration:
break
class Pandas:
def __init__(self):
self.data = [
{'name': 'Ivo', 'kg': 100},
{'name': 'Marto', 'kg': 80},
{'name': 'Pesho', 'kg': 120}
]
def __iter__(self):
self.index = 0
return self # Important!
def __next__(self):
index = self.index
self.index += 1
try:
return self.data[index]
except IndexError:
raise StopIteration
pandas = Pandas()
for panda in pandas:
print(panda)
╰─Ѫ py demo.py 1 ↵
{'name': 'Ivo', 'kg': 100}
{'name': 'Marto', 'kg': 80}
{'name': 'Pesho', 'kg': 120}
pandas = Pandas()
pandas_iterator = iter(pandas)
print(next(pandas_iterator))
╰─Ѫ py demo.py 1 ↵
{'name': 'Ivo', 'kg': 100}
class Wallet1:
def __init__(self, money):
self.money = money
def __iter__(self):
self.index = 0
return self
def __next__(self):
if self.index >= len(self.money):
raise StopIteration
element = self.money[self.index]
self.index += 1
return element
class Wallet2:
def __init__(self, money):
self.money = money
def __getitem__(self, index):
if index >= len(self.money):
raise StopIteration
return self.money[index]
money = [10, 10, 5, 100, 50, 20]
zipped_wallets = zip(Wallet1(money), Wallet2(money))
print([(x, y) for x, y in zipped_wallets])
╰─Ѫ py demo.py
[(10, 10), (10, 10), (5, 5), (100, 100), (50, 50), (20, 20)]
class OddNumbers:
def __iter__(self):
self.number = 1
return self
def __next__(self):
number = self.number
self.number += 2
return number
odd_numbers = iter(OddNumbers())
print(next(odd_numbers))
print(next(odd_numbers))
print(next(odd_numbers))
print(next(odd_numbers))
╰─Ѫ py demo.py 1 ↵
1
3
5
7
Laziness in programming usually means that the element is generated only when it's needed/invoked.
numbers = [[1, 2, 3], [1, 2, 3], [1, 2, 3]]
sums = (sum(x) for x in numbers)
numbers[2].append(100)
for x in sums:
print(x)
╰─Ѫ py demo.py
6
6
106
range
map
zip
filter
enumerate
Every function is executed from its first line. Then execution continues until:
An Exception is raised.
A return statement. Return means that the function returns control to the point where it was called.
A yield statement. Yield means that the function is "paused" in its current state. The transfer of control is temporary and the function expects to regain it in future.
def random_gen():
index = 1
print(f'Called for {index} time')
yield randint(1, 10)
index += 1
print(f'Called for {index} time')
yield randint(1, 10)
index += 1
print(f'Called for {index} time')
yield randint(1, 10)
index += 1
gen = random_gen()
print(next(gen))
print(next(gen))
print(next(gen))
╰─Ѫ py demo.py
Called for 1 time
5
Called for 2 time
6
Called for 3 time
8
for x in random_gen():
print(x)
╰─Ѫ py demo.py
Called for 1 time
7
Called for 2 time
6
Called for 3 time
4
def fib():
a, b = 1, 1
while a < 100:
yield a
a, b = b, a + b
for f in fib():
print(f)
╰─Ѫ py demo.py
1
1
2
3
5
8
13
21
34
55
89
def complex_generator():
items = [1, 2, 3]
wtf = 1
for item in items:
value = (yield item)
if value is not None:
wtf += value
print(wtf, item)
return 42
By Hack Bulgaria