| Date: | 2020-10-20 09:00 |
|---|---|
| summary: | Генераторы и цикл for. |
| Status: | published |
Contents
Цикл for может использоваться для различных целей.
Самый простой пример использования цикла:
for i in range(5):
print(i)0 1 2 3 4
При помощи этого цикла можно итерироваться по любому объекту-коллекции:
lst = ["qwerty", 12345, 34.42]
for i in lst:
print(i)qwerty 12345 34.42
Но в таком случае встает вопрос, что же общего между объектом-коллекцией
и диапазоном значений? range является функцией. Попробуем
посмотреть, что эта функция возвращает:
a = range(5)
print("object:\n\t", a)
print("type:\n\t", type(a))
print("Methods and attributes:\n\t", dir(a))object:
range(0, 5)
type:
<class 'range'>
Methods and attributes:
['__bool__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'count', 'index', 'start', 'step', 'stop']
То есть range -- это класс и мы вызываем его конструктор. Объект
этого класса является итерируемым, а значит с ним может работать цикл
for. Чтобы создать итератор из объекта, воспользуемся функцией
iter():
iterator = iter(a)
print("object:\n\t", iterator)
print("type:\n\t", type(iterator))
print("Methods and attributes:\n\t", dir(iterator))object:
<range_iterator object at 0x0000012FA12F9CF0>
type:
<class 'range_iterator'>
Methods and attributes:
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__length_hint__', '__lt__', '__ne__', '__new__', '__next__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setstate__', '__sizeof__', '__str__', '__subclasshook__']
Итератор — объект, который знает свое текущее состояние и может вычислить следующее значение. Такой подход не приводит к созданию дополнительных больших объектов в памяти и таким образом делает программу более эффективной. Никакой лишней информации при этом в памяти не хранится.
Для того, чтобы перейти к следующему состоянию, используется функция
next().
print(next(iterator))
print(next(iterator))
print(next(iterator))
print(next(iterator))
print(next(iterator))0 1 2 3 4
Но что же происходит, когда мы пытаемся получить следующий объект, но его не существует?
next(iterator)--------------------------------------------------------------------------- StopIteration Traceback (most recent call last) <ipython-input-19-4ce711c44abc> in <module>() ----> 1 next(iterator) StopIteration:
В таком случае выпадает ошибка StopIteration, которая говорит, что
следующий объект получить невозможно. Это и является признаком конца
итерации. На эту ошибку и ориентируется цикл for.
Вам дана функция на языке python:
def print_map(function, iterable):
for i in iterable:
print(function(i))
Требуется переписать данную функцию не используя цикл for. ****
Рассмотрим несколько примеров итерируемых объектов, которые есть в языке
python (кроме range).
map(function, iterable)
В начале рассмотрим функцию map(func, iterable). Эта функция
позволяет применить некоторую другую функцию func ко всем элементам
другого итерируемого объекта iterable. Обратите внимание, что
объект-функция передается без круглых скобок
def baz(value):
return value * value
lst = [1, 2, 3, 4, 5]
for i in map(baz, lst):
print(i)1 4 9 16 25
zip(iterable[, iterable, ...])
Функция zip(iterable[, iterable, ...]) позволяет параллельно
итерироваться по большому количеству итерируемых объектов, получая из
них соответствующие элементы в виде кортежа. Итератор прекращает свою
работу, когда один из переданных объектов закончится.
names = ["Alex", "Bob", "Alice", "John", "Ann"]
age = [25, 17, 34, 24, 42]
sex = ["M", "M", "F", "M", "F"]
for values in zip(names, age, sex):
print("name: {:>10} age: {:3} sex: {:2}".format(*values))name: Alex age: 25 sex: M name: Bob age: 17 sex: M name: Alice age: 34 sex: F name: John age: 24 sex: M name: Ann age: 42 sex: F
filter(func, iterable)
Пробегает по итерируемому объекту и возвращает только те элементы,
которые удовлетворяют условию, описанному в функции func.
def bar(x):
if abs((34-x*x))**0.5 > x:
return True
return False
for i in filter(bar, [0, 1, 2, 3, 4, 5]):
print(i)0 1 2 3 4
enumerate(iterable, start=0)
Принимает на вход итерируемый объект и возвращает пары (индекс элемента,
элемент). Индексация начинается со start, который по умолчанию равен 0.
names = ["Alex", "Bob", "Alice", "John", "Ann"]
for idx, elem in enumerate(names, 1):
print("{:02}: {:>7}".format(idx, elem))01: Alex 02: Bob 03: Alice 04: John 05: Ann
Кажется, что концепция генерации объектов налету, без предварительного выделения памяти под целый массив, является довольно удобной и полезной. Объекты-итераторы могут хранить, например, списки запросов к серверу, логи системы и другую информацию, которую можно обрабатывать последовательно. В таком случае, нам хочется научиться создавать подобные объекты.
Для этих целей может использоваться ключевое слово yield. Функция, в
которой содержится это ключевое слово, становится функцией-генератором.
Из такой функции можно создать объект-итератор. При вызове функции
next() выполнение этой функции дойдет до первого встреченного
ключевого слова yield, после чего, подобно действию return,
управление перейдет основной программе. Поток управления вернется обратно
в функцию при следующем вызове next() и продолжит выполнение с того
места, на котором остановился ранее.
Рассмотрим, каким образом можно написать свою собственную функцию
range():
def my_range(a, b=None, step=1):
if b is None:
a, b = 0, a
_current = a
while True:
yield _current
_next = _current + step
if (_next - b)*(_current - b) <= 0:
break
_current = _next
for i in my_range(5):
print(i, end = " ")
print()
for i in my_range(1, 5):
print(i, end = " ")
print()
for i in my_range(1, 10, 2):
print(i, end = " ")
print()
for i in my_range(10, 0, -3):
print(i, end = " ")
print()0 1 2 3 4 1 2 3 4 1 3 5 7 9 10 7 4 1
Напишите генератор, выводящий первые n чисел Фибоначчи. ***
Реализуйте аналог функций zip, map, enumerate. ***
Большое количество различных итерируемых объектов содержится в библиотеке itertools. Функции приведены в таблицах ниже:
| Iterator | Arguments | Results | Example |
|---|---|---|---|
| count()_ | start, [step] | start, start+step, start+2*step, … | count(10) --> 10 11 12 13 14 ... |
| cycle()_ | p | p0, p1, … plast, p0, p1, … | cycle('ABCD') --> A B C D A B C D ... |
| repeat()_ | elem [,n] | elem, elem, elem, … endlessly or up to n times | repeat(10, 3) --> 10 10 10 |
| Iterator | Arguments | Results | Example |
|---|---|---|---|
| accumulate()_ | p [,func] | p0, p0+p1, p0+p1+p2, … | accumulate([1,2,3,4,5]) --> 1 3 6 10 15 |
| chain()_ | p, q, … | p0, p1, … plast, q0, q1, … | chain('ABC', 'DEF') --> A B C D E F |
| chain.from_iterable()_ | iterable | p0, p1, … plast, q0, q1, … | chain.from_iterable(['ABC', 'DEF']) --> A B C D E F |
| compress()_ | data, selectors | (d[0] if s[0]), (d[1] if s[1]), … | compress('ABCDEF', [1,0,1,0,1,1]) --> A C E F |
| dropwhile()_ | pred, seq | seq[n], seq[n+1], starting when pred fails | dropwhile(lambda x: x<5, [1,4,6,4,1]) --> 6 4 1 |
| filterfalse()_ | pred, seq | elements of seq where pred(elem) is false | filterfalse(lambda x: x%2, range(10)) --> 0 2 4 6 8 |
| groupby()_ | iterable[, key] | sub-iterators grouped by value of key(v) | |
| islice()_ | seq, [start,] stop [, step] | elements from seq[start:stop:step] | islice('ABCDEFG', 2, None) --> C D E F G |
| starmap()_ | func, seq | func(*seq[0]), func(*seq[1]), … | starmap(pow, [(2,5), (3,2), (10,3)]) --> 32 9 1000 |
| takewhile()_ | pred, seq | seq[0], seq[1], until pred fails | takewhile(lambda x: x<5, [1,4,6,4,1]) --> 1 4 |
| tee()_ | it, n | it1, it2, … itn splits one iterator into n | |
| zip_longest()_ | p, q, … | (p[0], q[0]), (p[1], q[1]), … | zip_longest('ABCD', 'xy', fillvalue='-') --> Ax By C- D- |
| Iterator | Arguments | Results |
|---|---|---|
| product()_ | p, q, … [repeat=1] | cartesian product, equivalent to a nested for-loop |
| permutations()_ | p[, r] | r-length tuples, all possible orderings, no repeated elements |
| combinations()_ | p, r | r-length tuples, in sorted order, no repeated elements |
| combinations_with_replacement()_ | p, r | r-length tuples, in sorted order, with repeated elements |
| Examples | Results |
|---|---|
| product('ABCD', repeat=2) | AA AB AC AD BA BB BC BD CA CB CC CD DA DB DC DD |
| permutations('ABCD', 2) | AB AC AD BA BC BD CA CB CD DA DB DC |
| combinations('ABCD', 2) | AB AC AD BC BD CD |
| combinations_with_replacement('ABCD',2) | AA AB AC AD BB BC BD CC CD DD |
Написать функцию, принимающую 2 списка и возвращающую декартово произведение (использовать itertools.product)
def get_cartesian_product(a, b):
raise RuntimeError("Not implemented")
get_cartesian_product([1, 2], [3, 4]) == [(1, 3), (1, 4), (2, 3), (2, 4)]Написать функцию, принимающую строку s и число n и возвращающую всевозможные перестановки из n символов в s строке в лексикографическом(!) порядке (использовать itertools.permutations)
def get_permutations(s, n):
raise RuntimeError("Not implemented")
get_permutations("cat", 2) == ["ac", "at", "ca", "ct", "ta", "tc"]Реализовать функцию get_combinations. Должна принимать строку s и число k и возвращать все возможные комбинации из символов в строке s с длинами <= k (использовать itertools.combinations)
def get_combinations(s, n):
raise RuntimeError("Not implemented")
get_combinations("cat", 2) == ["a", "c", "t", "ac", "at", "ct"]Функция должна принимать строку s и число k и возвращать все возможные комбинации из символов в строке s с длинами = k с повторениями (использовать itertools.combinations_with_replacement)
def get_combinations_with_r(s, n):
raise RuntimeError("Not implemented")
get_combinations_with_r("cat", 2) == ["aa", "ac", "at", "cc", "ct", "tt"]Написать функцию, которая подсчитывает количество подряд идующих символов в строке (использовать itertools.groupby)
def compress_string(s):
raise RuntimeError("Not implemented")
compress_string('1222311') == [(1, 1), (3, 2), (1, 3), (2, 1)]В функцию передается список списков. Нужно вернуть максимум, который достигает выражение (a\_1^2 + a\_2^2 + ... + a\_n^2) \% m. Где a_i --- максимальный элемент из i-ого списка (использовать функцию из itertools)
def maximize(lists, m):
raise RuntimeError("Not implemented")
lists = [
[5, 4],
[7, 8, 9],
[5, 7, 8, 9, 10]
]
maximize(lists, m=1000) == 206В примере = 206, так как это максимум от суммы (a_1^2 + a_2^2 + a_3^2) \% 1000
a_1 = 5, a_2 = 9, a_3 = 10