Встроенные типы данных (их назначение, методы и стандартное поведение) - введение
Изменяемые и неизменяемые типы данных
В пайтоне, как и в других высокоуровневых языках, существуют изменяемые (mutable) и неизменяемые (immutable) типы данных.
К стандартным встроенным неизменяемым типам относятся:
None
bool - True/False
числа - int(), float(), complex()
строки - str()
кортежи - tuple()
неизменяемые ("замороженные") множества - frozenset()
байты - bytes()
К изменяемым:
списки - list()
множества - set()
словари - dict()
списки байтов - bytearray()
Что это значит простыми словами? Неизменяемый тип данных нельзя изменить без потери первоначального объекта. Изменяемый - можно менять на лету без потери объекта.
Вспомним пример из вводной статьи про пятёрку:
>>> a = 5
>>> id(a)
1609562080
Заменяя пятёрку на любое другое число, мы теряем эту пятёрку, и переменная "а" начинает ссылаться на новое число, которое будет храниться в новой ячейке памяти:
>>> a = 6
>>> id(a)
1411840000
Тоже самое происходит с любым из объектов неизменяемого типа: если нам нужно изменить содержимое - мы либо создаём новый на основе старого без его изменения, либо неизбежно теряем старый. Как это происходит - мы увидим чуть ниже.
С изменяемыми в этом плане всё куда проще - объект остаётся тем же самым, чтобы мы с ним ни делали, лишь бы не удаляли полностью.
>>> lst = [1, 2, 3] # создаём список из трёх чисел
>>> id(lst) # запрашиваем номер ячейки памяти, где он сохранён
49718408 # получаем ответ на запрос
>>> lst # просим вывести переменную на печать
Прим.: в коде вывод на печать - только через print(), в консоли можно и так, и так: и через "print()", и через "переменная+Enter"
[1, 2, 3] # вывод на экран содержимого переменной "lst"
>>> lst.clear() # очищаем список полностью, делая его пустым
>>> lst
[] # список опустел
>>> id(lst)
49718408 # адрес ячейки не изменился - значит объект всё тот же
Т.е. этот список можно заполнять, опустошать, выдирать из него или вставлять в него элементы по одному или пачками - сам объект от этого не изменится, мы его не потеряем и не создадим новый за счёт этих изменений.
Однако, есть нюанс: если всегда помнить, что переменные - это ссылки, то нужно также понимать, что объекты нельзя копировать через назначение новой ссылки.
>>> a = 5
>>> b = a
>>> a
5
>>> b
5
>>> id(a)
1407776752
>>> id(b)
1407776752
>>> b + 1
>>> a
5
>>> b
6
>>> id(a)
1407776752
>>> id(b)
1407776768
Из этого примера видно, что сначала обе переменные ссылаются на одну и ту же пятёрку. Затем мы к "b" прибавили единицу, и, о чудо, в ней оказалась шестёрка, а шестёрка - это новый объект, у которого своё место в памяти. При этом "a" никуда не делась, не изменилась и осталась ссылаться на пятёрку.
Подобный фокус с изменяемыми типами не пройдёт.
>>> lst = [1, 2, 3]
>>> new_lst = lst
>>> id(lst)
49718216
>>> id(new_lst)
49718216
>>> lst
[1, 2, 3]
>>> new_lst
[1, 2, 3]
>>> new_lst.clear()
>>> new_lst
[]
>>> lst
[]
>>> id(lst)
49718216
>>> id(new_lst)
49718216
Попытка получить новый пустой список из старого привела к тому, что мы, во-первых, удалили старый и, во-вторых, не получили никакого нового. Обе переменные как ссылались на один и тот же объект - так и ссылаются.
Для копирования изменяемых типов существует встроенный модуль "copy". Цитата из официальной документации модуля:
DESCRIPTION
Interface summary:
import copy
x = copy.copy(y) # make a shallow copy of y (поверхностное копирование)
x = copy.deepcopy(y) # make a deep copy of y (глубокое копирование вложенных структур)
Т.е. здесь уже по названию самих переменных понятно, что в результате создаются новые объекты.
И ещё разок: назначение нескольких переменных для одного объекта - это НЕ КОПИРОВАНИЕ, т.к. вследствие этих действий вы не создаёте новый объект. Копирование неизменяемых объектов в принципе невозможно, так как неизменяемый объект уникален, и даже применение модуля copy лишь создаст новую ссылку на этот объект.
>>> a = 5
>>> b = copy.copy(a)
>>> a
5
>>> b
5
>>> id(a)
1403844592
>>> id(b)
1403844592
Копировать в чистом виде можно только изменяемые объекты.
>>> lst = [1,2,3]
>>> new_lst = copy.copy(lst)
>>> lst
[1, 2, 3]
>>> new_lst
[1, 2, 3]
>>> id(lst)
49634696
>>> id(new_lst)
49794312
Коллекции, последовательности
Коллекциями стандартно называются объекты, содержащие упорядоченный или неупорядоченный набор элементов разных типов, в том числе типов, одинаковых с типом самой коллекции (например: списки списков, кортежи кортежей и т.п.).
ВИДЫ КОЛЛЕКЦИЙ
NB!: строки также являются последовательностями
Последовательности - это упорядоченные коллекции, то есть элементы коллекции хранятся в строгом порядке. Такие коллекции поддерживают индексацию элементов и срезы.
Индекс - порядковый номер элемента в последовательности, начиная с нулевого (а не с первого, как нам было бы привычнее).
Срез - выборка элементов из последовательности в формате [start:stop:step], т.е.:
- стартовый индекс,
- конечный индекс (невключительно),
- шаг (необязательный параметр, по умолчанию равен единице).
Если задан только один параметр среза без двоеточий, то он по умолчанию считается просто порядковым индексом.
>>> s = "Hello, world!" # строки - разновидность последовательностей
>>> s[0] # первый элемент строки
'H'
>>> s[-1] # последний элемент строки
'!'
>>> s[-2] # предпоследний элемент строки
'd'
>>> s[2:-2] # срез строки c ТРЕТЬЕГО (считаем от нуля) по предпоследний (НЕВКЛЮЧИТЕЛЬНО) элементы
'llo, worl'
>>> s[1:-1:3] # срез строки cо ВТОРОГО (считаем от нуля) по последний (НЕВКЛЮЧИТЕЛЬНО) элементы с шагом "каждый третий (в человеческом понимании) элемент от старта, включая стартовый"
'eowl'
Если задан один параметр среза с двоеточиями, то в зависимости от его местоположения он может выступать стартом, стопом или шагом:
>>> s[2:] # Срез от третьего элемента до конца последовательности
'llo, world!'
>>> s[:2] # Срез начала последовательности до третьего элемента невключительно
'He'
>>> s[::2] # Срез последовательности с шагом "каждый второй элемент, включая первый"
'Hlo ol!'
Шаг может быть также отрицательным. Тогда последовательность обходится с конца (в обратном порядке).
>>> s[::-1] # Срез с шагом "каждый элемент в обратном порядке"
'!dlrow ,olleH'
>>> s[::-2] # Срез с шагом "каждый второй элемент в обратном порядке, начиная с последнего"
'!lo olH'
Все остальные виды коллекций - неупорядоченные (исключение - OrderedDict из модуля collections, но в данном случае он нас не интересует) , то есть вызвать элемент по его индексу из множества или словаря невозможно, равно как и сделать их срез.
Кроме того, передавая неупорядоченную коллекцию в print не стоит полагаться на тот порядок, который перед вами возник. Даже если вдруг 10000 раз подряд вы отпринтовали одно и то же множество, и оно у вас вывелось в одном и том же порядке, то рано или поздно оно предстанет в совершенно другом виде - это случится неизбежно. Если вы не будете этого учитывать, то ваш код рано или поздно сломается.
-----
Всё это, конечно же, дублируется в отдельном канале в телеге. По всем вопросам обращайтесь через Telegram.
Да, и тег моё - потому что всё написано моими руками, а не тупо понакопировано с других сайтов.
P.S. Большое спасибо всем моим подписчикам за поддержку и активность! Без вас я, возможно, не решился бы продолжать.
Ссылки на предыдущие посты:
Программирование на python
831 пост11.9K подписчиков
Правила сообщества
Публиковать могут пользователи с любым рейтингом. Однако!
Приветствуется:
• уважение к читателям и авторам
• конструктивность комментариев
• простота и информативность повествования
• тег python2 или python3, если актуально
• код публиковать в виде цитаты, либо ссылкой на специализированный сайт
Не рекомендуется:
• допускать оскорбления и провокации
• распространять вредоносное ПО
• просить решить вашу полноценную задачу за вас
• нарушать правила Пикабу