- PVSM.RU - https://www.pvsm.ru -
Данная статья является продолжением моей статьи "Python: коллекции, часть 1: классификация, общие подходы и методы, конвертация [1]".
В данной статье мы продолжим изучать общие принципы работы со стандартными коллекциями (модуль collections в ней не рассматривается) Python.
Для кого: для изучающих Python и уже имеющих начальное представление о коллекциях и работе с ними, желающих систематизировать и углубить свои знания, сложить их в целостную картину.
Рассмотрим индексированные коллекции (их еще называют последовательности — sequences) — список (list), кортеж (tuple), строку (string).
Под индексированностью имеется ввиду, что элементы коллекции располагаются в определённом порядке, каждый элемент имеет свой индекс от 0 (то есть первый по счёту элемент имеет индекс не 1, а 0) до индекса на единицу меньшего длины коллекции (т.е. len(mycollection)-1).
Для всех индексированных коллекций можно получить значение элемента по его индексу в квадратных скобках. Причем, можно задавать отрицательный индекс, это значит, что будем находить элемент с конца считая обратном порядке.
При задании отрицательного индекса, последний элемент имеет индекс -1, предпоследний -2 и так далее до первого элемента индекс которого равен значению длины коллекции с отрицательным знаком, то есть (-len(mycollection).
элементы | a | b | c | d | e |
---|---|---|---|---|---|
индексы | 0 (-5) | 1 (-4) | 2 (-3) | 3 (-2) | 4 (-1) |
my_str = "abcde"
print(my_str[0]) # a - первый элемент
print(my_str[-1]) # e - последний элемент
print(my_str[len(my_str)-1]) # e - так тоже можно взять последний элемент
print(my_str[-2]) # d - предпоследний элемент
Наши коллекции могут иметь несколько уровней вложенности, как список списков в примере ниже. Для перехода на уровень глубже ставится вторая пара квадратных скобок и так далее.
my_2lvl_list = [[1, 2, 3], ['a', 'b', 'c']]
print(my_2lvl_list[0]) # [1, 2, 3] - первый элемент — первый вложенный список
print(my_2lvl_list[0][0]) # 1 — первый элемент первого вложенного списка
print(my_2lvl_list[1][-1]) # с — последний элемент второго вложенного списка
Поскольку кортежи и строки у нас неизменяемые коллекции, то по индексу мы можем только брать элементы, но не менять их:
my_tuple = (1, 2, 3, 4, 5)
print(my_tuple[0]) # 1
my_tuple[0] = 100 # TypeError: 'tuple' object does not support item assignment
А вот для списка, если взятие элемента по индексу располагается в левой части выражения, а далее идёт оператор присваивания =, то мы задаём новое значение элементу с этим индексом.
my_list = [1, 2, 3, [4, 5]]
my_list[0] = 10
my_list[-1][0] = 40
print(my_list) # [10, 2, 3, [40, 5]]
Очень часто, надо получить не один какой-то элемент, а некоторый их набор ограниченный определенными простыми правилами — например первые 5 или последние три, или каждый второй элемент — в таких задачах, вместо перебора в цикле намного удобнее использовать так называемый срез (slice, slicing).
Следует помнить, что взяв элемент по индексу или срезом (slice) мы не как не меняем исходную коллекцию, мы просто скопировали ее часть для дальнейшего использования (например добавления в другую коллекцию, вывода на печать, каких-то вычислений). Поскольку сама коллекция не меняется — это применимо как к изменяемым (список) так и к неизменяемым (строка, кортеж) последовательностям.
Синтаксис среза похож на таковой для индексации, но в квадратных скобках вместо одного значения указывается 2-3 через двоеточие:
my_collection[start:stop:step] # старт, стоп и шаг
col = 'abcdefg'
print(col[:]) # abcdefg
print(col[::-1]) # gfedcba
print(col[::2]) # aceg
print(col[1::2]) # bdf
print(col[:1]) # a
print(col[-1:]) # g
print(col[3:4]) # d
print(col[-3:]) # efg
print(col[-3:1:-1]) # edc
print(col[2:5]) # cde
Чтобы избавится от «магических констант», особенно в случае, когда один и тот же срез надо применять многократно, можно задать константы с именованными срезами с пользованием специальной функции slice()()
Примечание: Nonе соответствует опущенному значению по-умолчанию. То есть [:2] становится slice(None, 2), а [1::2] становится slice(1, None, 2).
person = ('Alex', 'Smith', "May", 10, 1980)
NAME, BIRTHDAY = slice(None, 2), slice(2, None)
# задаем константам именованные срезы
# данные константы в квадратных скобках заменятся соответствующими срезами
print(person[NAME]) # ('Alex', 'Smith')
print(person[BIRTHDAY]) # ('May', 10, 1980)
my_list = [1, 2, 3, 4, 5, 6, 7]
EVEN = slice(1, None, 2)
print(my_list[EVEN]) # [2, 4, 6]
Важный момент, на котором не всегда заостряется внимание — с помощью среза можно не только получать копию коллекции, но в случае списка можно также менять значения элементов, удалять и добавлять новые.
my_list = [1, 2, 3, 4, 5]
# my_list[1:2] = 20 # TypeError: can only assign an iterable
my_list[1:2] = [20] # Вот теперь все работает
print(my_list) # [1, 20, 3, 4, 5]
my_list = [1, 2, 3, 4, 5]
my_list[5:] = [6] # вставляем в конец — лучше использовать .append(6)
print(my_list) # [1, 2, 3, 4, 5, 6]
my_list[0:0] = [0] # вставляем в начало — лучше использовать .insert(0, 0)
print(my_list) # [0, 1, 2, 3, 4, 5, 6]
my_list[3:3] = [25] # вставляем между элементами — лучше использовать .insert(3, 25)
print(my_list) # [0, 1, 2, 25, 3, 4, 5, 6]
my_list = [1, 2, 3, 4, 5]
my_list[1:3] = [20, 30]
print(my_list) # [1, 20, 30, 4, 5]
my_list[1:3] = [0] # нет проблем заменить два элемента на один
print(my_list) # [1, 0, 4, 5]
my_list[2:] = [40, 50, 60] # или два элемента на три
print(my_list) # [1, 0, 40, 50, 60]
my_list = [1, 2, 3, 4, 5]
my_list[:2] = []
print(my_list) # [3, 4, 5]
Обращение по индексу по сути является частным случаем среза, когда мы обращаемся только к одному элементу, а не диапазону. Но есть очень важное отличие в обработке ситуации с отсутствующим элементом с искомым индексом.
Обращение к несуществующему индексу коллекции вызывает ошибку:
my_list = [1, 2, 3, 4, 5]
print(my_list[-10]) # IndexError: list index out of range
print(my_list[10]) # IndexError: list index out of range
А в случае выхода границ среза за границы коллекции никакой ошибки не происходит:
my_list = [1, 2, 3, 4, 5]
print(my_list[0:10]) # [1, 2, 3, 4, 5] — отработали в пределах коллекции
print(my_list[10:100]) # [] - таких элементов нет — вернули пустую коллекцию
print(my_list[10:11]) # [] - проверяем 1 отсутствующий элемент - пустая коллекция, без ошибки
Сортировка элементов коллекции важная и востребованная функция, постоянно встречающаяся в обычных задачах. Тут есть несколько особенностей, на которых не всегда заостряется внимание, но которые очень важны.
Мы может использовать функцию sorted() для вывода списка сортированных элементов любой коллекции для последующее обработки или вывода.
my_list = [2, 5, 1, 7, 3]
my_list_sorted = sorted(my_list)
print(my_list_sorted) # [1, 2, 3, 5, 7]
my_set = {2, 5, 1, 7, 3}
my_set_sorted = sorted(my_set, reverse=True)
print(my_set_sorted) # [7, 5, 3, 2, 1]
Пример сортировки списка строк по длине len() каждого элемента:
my_files = ['somecat.jpg', 'pc.png', 'apple.bmp', 'mydog.gif']
my_files_sorted = sorted(my_files, key=len)
print(my_files_sorted) # ['pc.png', 'apple.bmp', 'mydog.gif', 'somecat.jpg']
Функция reversed() применяется для последовательностей и работает по другому:
my_list = [2, 5, 1, 7, 3]
my_list_sorted = reversed(my_list)
print(my_list_sorted) # <listreverseiterator object at 0x7f8982121450>
print(list(my_list_sorted)) # [3, 7, 1, 5, 2]
print(my_list[::-1]) # [3, 7, 1, 5, 2] - тот же результат с помощью среза
У списка (и только у него) есть особые методы .sort() и .reverse() которые делают тоже самое, что соответствующие функции sorted() и reversed(), но при этом:
my_list = [2, 5, 1, 7, 3]
my_list.sort()
print(my_list) # [1, 2, 3, 5, 7]
Обратите внимание: Частая ошибка начинающих, которая не является ошибкой для интерпретатора, но приводит не к тому результату, который хотят получить.
my_list = [2, 5, 1, 7, 3]
my_list = my_list.sort()
print(my_list) # None
В сортировке словаря есть свои особенности, вызванные тем, что элемент словаря — это пара ключ: значение.
my_dict = {'a': 1, 'c': 3, 'e': 5, 'f': 6, 'b': 2, 'd': 4}
mysorted = sorted(my_dict)
print(mysorted) # ['a', 'b', 'c', 'd', 'e', 'f']
mysorted = sorted(my_dict.items())
print(mysorted) # [('a', 1), ('b', 2), ('c', 3), ('d', 4), ('e', 5), ('f', 6)]
mysorted = sorted(my_dict.values())
print(mysorted) # [1, 2, 3, 4, 5, 6]
Отдельные сложности может вызвать сортировка словаря не по ключам, а по значениям, если нам не просто нужен список значений, и именно выводить пары в порядке сортировки по значению.
Для решения этой задачи можно в качестве специальной функции сортировки передавать lambda-функцию lambda x: x[1] которая из получаемых на каждом этапе кортежей (ключ, значение) будет брать для сортировки второй элемент кортежа.
population = {"Shanghai": 24256800, "Karachi": 23500000, "Beijing": 21516000, "Delhi": 16787941}
# отсортируем по возрастанию населения:
population_sorted = sorted(population.items(), key=lambda x: x[1])
print(population_sorted)
# [('Delhi', 16787941), ('Beijing', 21516000), ('Karachi', 23500000), ('Shanghai', 24256800)]
Автор: DaneSoul
Источник [2]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/python/232507
Ссылки в тексте:
[1] Python: коллекции, часть 1: классификация, общие подходы и методы, конвертация: https://habrahabr.ru/post/319164/
[2] Источник: https://habrahabr.ru/post/319200/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best
Нажмите здесь для печати.