Доброго времени суток, читатели, зрители моего канала programmer's notes.
Функции для работы с итерируемыми объектами
Несколько замечаний
Когда мы имеем дело с итерируемыми объектами, то нужно иметь в виду, что не все функции, которые применяются для последовательностей (коллекций), одинаково работают с разными объектами, если вообще работают. Например, функция reversed(), которая меняет порядок списка на противоположный, не применима к множеству, так как там нет порядка. Не применима она также к итераторам, но работает с range(). Функция len() тоже с итераторами не работает, но работает с другими итерируемыми объектами.
Функции sum(), min(), max() работают со всеми итерируемыми объектами, но есть определенное своеобразие в их работе с итераторами. Поскольку для получения значений для этих функций нужно перебрать все элементы, то в результате функция возвращает нужное значение, но итератор становится пустым. Это главное свойство любого итератора. Второй момент, который следует помнить, заключается в том, что итератор в принципе может быть и бесконечным. Тогда бессмысленно использовать эти функции.
it = iter([0, 1, 3])
print(sum(it))
print(list(it))
Результат выполнения фрагмента
4
[]
И это понятно, итератор стал пустым.
Функции all, any
Обе функции all() и any() возвращают логическое значение True или False. Применимы они к итерируемым объектам. Первая функция возвращает True, если все элементы сводятся к True. Напоминаю вам, что все стандартные типы данных могут быть преобразованы к логическим типам. К True сводятся ненулевые числа, не пустые строки, не пустые коллекции и т.п. any() - возвращает True, если найдется хотя бы один элемент, сводящийся к True. Т.е. в первой функции алгоритм должен предполагать просмотр всех элементов, если только не встретится элемент, сводимый к False, а во второй, до элемента, который сводится к True.
it = iter([0, 1, 3])
print(any(it))
print(list(it))
it = iter([0, 1, 3])
print(all(it))
print(list(it))
Результат выполнения фрагмента
True
[3]
False
[1, 3]
Т.е. any() перестала просматривать итератор, как только встретила 1, а all() перестала просматривать итератор, как только встретила 0. И опять же, а вдруг итератор окажется бесконечным? Кстати это относится и к функциям, преобразующим итераторы к коллекциям (list(), set()).
И на закуску проверка того, что введенная строка является ip адресом (IPv4)
print(all(map(lambda x: x.isdigit() and int(x) <= 255, input().replace(' ','').split('.'))))
Но есть нюанс, количество компонент не проверяется. Ну есть над чем подумать.
Функция enumarate()
Функция действует на итерируемый объект и нумерует его элементы. При этом сама она возвращает итератор, так что при необходимости ее преобразовывают в список. Чтобы не быть голословным, вот пример
ll = enumerate(['qwerty', 'asdfg', 'zxcvb'])
print(list(ll))
Результат выполнения фрагмента
[(0, 'qwerty'), (1, 'asdfg'), (2, 'zxcvb')]
т.е. мы получили список кортежей. Нумерация, как видим, идет с нуля, но можно указать начальный номер вторым аргументом функции enumerate().
Результат выполнения программы
1. qwerty
2. asdfg
3. zxcvb
А можно и одной строкой
print(*[str(i) + '. ' + str(v) for i, v in enumerate(['qwerty', 'asdfg', 'zxcvb'], 1)], sep='\n')
Эх, чувствую, что будут меня ругать некоторые, что я сбился с пути истинного и сбиваю своих читателей на однострочные программы, которые сам же и ругал когда-то. :)
Но продолжу соблазнять читателя
Вот двухстрочный вариант проверки на ip-адрес (см. выше), но уже с проверкой на количество компонентов.
p = list(enumerate(map(lambda x: x.isdigit() and int(x) <= 255, input().replace(' ','').split('.'))))
print(all([r[1] for r in (p if p[-1][0] == 3 else [(0, False)])]))
Из двух строк, правда.
Функция zip
Ну и наконец добрались до функции zip(), весьма таки полезной в программировании. Функция объединяет произвольное количество итерируемых объектов.
z1 = [1, 2, 3, 4]
z2 = ['q', 'w', 'e', 'r']
z3 = [2.3, 5.1, 6.8, 9.1]
z = list(zip(z1, z2, z3))
print(z)
Результат выполнения функции
[(1, 'q', 2.3), (2, 'w', 5.1), (3, 'e', 6.8), (4, 'r', 9.1)]
Т.е. мы получили список кортежей. Если объединяемые объекты разной длины, то результат обрезается по самому короткому. Если объект всего один, то мы получаем одноэлементные кортежи. Пустая функция zip() возвращает пустой итератор.
Забавно, но процесс применения функции можно легко обратить
z1 = [1, 2, 3, 4]
z2 = ['q', 'w', 'e', 'r']
z3 = [2.3, 5.1, 6.8, 9.1]
z = list(zip(z1, z2, z3))
z11, z22, z33 = zip(*z)
z11, z22, z33 = list(z11), list(z22), list(z33)
print(z11, z22, z33, sep='\n')
Результат выполнения
[1, 2, 3, 4]
['q', 'w', 'e', 'r']
[2.3, 5.1, 6.8, 9.1]
Т.е. мы вернулись к исходным значениям. Звёздочка, все дело в ней.
Функцию zip() традиционно используют для сокращения некоторых алгоритмов. Рассмотрим несколько типовых.
Сортировка несколько связанных списков. Иногда есть несколько списков и они жестко связаны друг с другом. Например в одном наименования товара, а в другом соответственно цена: товар с индексом 0 имеет цену из второго списка, который имеет значение индекса 0 и т.д. Если нужно отсортировать по имени или по цене, то сортировку нужно проделывать совместно, так как изменение положения элемента в одном списке, должно приводить к изменению соответствующего элемента в другом списке. Решение очень элегантно.
z1 = ['Хлеб', 'Колбаса', 'Сахар', 'Консервы', 'Арбуз']
z2 = [30, 300, 100, 124, 80]
z11, z22 = zip(*sorted(zip(z1, z2), key=lambda x:x[0]))
z1, z2 = list(z11), list(z22)
print(z1, z2, sep='\n')
Т.е. сортировка идет списка кортежей с указанием ключа. Можно сортировать хоть по названию, хоть по ценам, главное правильно указать ключ.
Результат выполнения программы
['Арбуз', 'Колбаса', 'Консервы', 'Сахар', 'Хлеб']
[80, 300, 124, 100, 30]
Кстати, функцию enumarate() можно легко реализовать с помощью zip()
z1 = ['qwerty', 'asdfg', 'zxcvb']
en = zip([i for i in range(1, len(z1) + 1)], z1)
print(list(en))
Результат выполнения фрагмента
[(1, 'qwerty'), (2, 'asdfg'), (3, 'zxcvb')]
Вот так!
Еще одно применение zip(). Работа со словарями.
Делаем словарь из двух связанных списков
z1 = ['Хлеб', 'Колбаса', 'Сахар', 'Консервы', 'Арбуз']
z2 = [30, 300, 100, 124, 80]
d1 = dict(zip(z1, z2))
print(d1)
Результат выполнения фрагмента
{'Хлеб': 30, 'Колбаса': 300, 'Сахар': 100, 'Консервы': 124, 'Арбуз': 80}
Ну и напоследок совсем замечательный пример, хотя и широко известный в интернете: транспонирование матриц. Матрица, как вы понимаете, двумерный список, у которого внутренние элементы списка должны иметь одинаковую длину. Но это ведь можно рассматривать как результат работы функции zip() (sic!).
zz = [[1, 2], [3, 4], [5, 6]]
zzt = [list(t) for t in zip(*zz)]
print(zzt)
Результат выполнения программы
[[1, 3, 5], [2, 4, 6]]
Ну очень элегантное решение.
Пока все. Базовый курс Python закончен. Вас ждут продолжения.
Но будет еще видеоурок о программировании в Linux, где расскажу как программирую я. Может также добавлю еще чего нибудь к этому курсу, чтобы сделать его самодостаточным.
Всего наилучшего. Оставляйте свои комментарии, не забывайте про лайки и подписывайтесь на мой канал programmer's notes.