Данные в языке Python делятся на две группы: простые (обозначают единственное значение) и составные (хранят в себе несколько значений). Рассмотрим основные типы данных языка Python.
int
— целое число.float
— вещественное (дробное, действительное) число.str
— строка.bool
— булевский (логический) тип.list
— список, изменяемый набор значений, пронумерованных целыми
числами, начиная с 0
.tuple
— кортеж, неизменяемый набор значений, пронумерованных целыми
числами, начиная с 0
.dict
— словарь (ассоциативный массив) — изменяемый набор значений,
индексируемый неизменяемыми типами данных (числа, строки, кортежи).set
— множество — неупорядоченный набор значений без повторений.int
)Целые числа могут быть записаны в разных системах счисления. В десятичной форме
они записываются как последовательности десятичных цифр. В шестнадцатеричной
форме — как последовательности шестнадцатеричных цифр (0
…9
, A
…F
),
предварённых знаком 0x
. Восьмеричные числа — последовательности восьмеричных
цифр, начинаются на 0t
. Двоичные — начинаются 0b
.
>>> 10
10
>>> 0xA
10
>>> 0t12
10
>>> 0b1010
10
Если у операций +
, -
, *
, //
, %
оба операнда целые, то результат будет
целым. Если хотя бы один из операндов вещественный — результат вещественный.
Результат операции /
всегда вещественный.
Размер целых чисел неограничен. Длина целого числа ограничивается лишь доступной памятью компьютера.
Тип целых чисел в Python называется int
. Одновременно, int
можно
как встроенную функцию, преобразующую свой аргумент в целое число:
>>> int(3.1415926)
3
>>> int('1000000')
1000000
Вещественные числа в Python моделируют действительные числа в математике. В отличие от целых чисел, вещественные числа имеют ограниченный диапазон и ограниченную точность.
Диапазон от 10−323 до 10+308. Если при расчётах получится меньшее число, то оно представляется как ноль, если большее — произойдёт ошибка переполнения. Точность ограничена ≈15 десятичными знаками после запятой.
Нижняя граница:
>>> 10.0**-323
1e-323
>>> 10.0**-324
0.0
Верхняя граница:
>>> 10.0**308
1e+308
>>> 10.0**309
Traceback (most recent call last):
File "<pyshell#24>", line 1, in <module>
10.0**309
OverflowError: (34, 'Result too large')
Точность:
>>> 1.0 + 0.000000000000001
1.000000000000001
>>> 1.0 + 0.0000000000000001
1.0
Вещественные числа, в отличие от целых, всегда содержат или знак разделителя
целой и дробной части (точку, .
), или показатель степени (e
). Разделителем
целой и дробной части является точка, т.к. запятая используется для разделения
параметров функции и при построении составных типов данных (массивы, кортежи
и т.д.)
Вещественное число может быть записано в показательной форме в виде
‹мантисса›e‹степень›
которое означает ‹мантисса›
×10‹степень›. Примеры:
6.022e23 # число Авогадро
1.38e-23 # постоянная Больцама
3e8 # округлённое значение скорости света в м/с
Класс вещественных чисел в Python называется float
, он же — встроенная
функция-конструктор, преобразующая свой аргумент к вещественному числу:
>>> float('3.1415926')
3.1415926
>>> float('10000000000000000000000000000000000000000')
1e+40
math
)В Python есть библиотека, содержащая основные математические функции,
работающие с вещественными числами: тригонометрия, логарифмы и т.д.
Называется она math
, подключается к программе как
from math import * # №1
from math import sin, pi # №2
import math # №3
В форме №1 в программе становятся доступными все константы и функции, определённые в библиотеке.
from math import *
print(sin(pi/2))
print(cos(pi/2))
В форме №2 — те, что явно перечислены после ключевого слова import
.
from math import sin, pi
print(sin(pi/2))
print(cos(pi/2)) # ОШИБКА! cos не виден!
В форме №3 — в программе доступен объект math
, функции библиотеки
доступны как поля и методы этого объекта.
import math
print(math.sin(math.pi/2))
print(math.cos(math.pi/2))
Функции библиотеки можно посмотреть во встроенной документации:
>>> import math
>>> help(math)
‹длинная справка›
>>> help(math.sin)
Help on built-in function sin in module math:
sin(x, /)
Return the sine of x (measured in radians).
Для преобразования, во-первых, можно использовать конструкторы float
и int
. В библиотеке math
есть функции floor
, ceil
и round
.
Разобраться с ними самостоятельно.
abs(x)
Функция abs(x)
возвращает абсолютное значение числа:
>>> abs(7)
7
>>> abs(-7)
7
>>> abs(-273.15)
273.15
Строка (класс str
) представляет последовательность печатных знаков. Знаки,
которые представляют строки в Python 3, являются символами Юникода, т.е.
включают в себя буквы разных алфавитов, цифры, знаки препинания, иероглифы,
пиктограммы, математические знаки и эмодзи. В противоположность, в Python 2.7
строки состояли из однобайтовых символов конкретной кодировки (например,
кодировки Windows-1251 на Windows в русскоязычной локализации).
Строки занимают промежуточное положение между простыми и составными типами
данных. С одной стороны, они хранят несколько значений. А с другой стороны,
у этих значений нет собственного типа — операция индексации s[i]
возвращает
не печатный символ, а строку из одного знака.
Строки — неизменяемый тип данных.
Конструктор строки — функция str
, которая для аргумента любого типа построит
строку. Если ей передать строку — то вернётся точная копия строки.
>>> str(True)
'True'
>>> str(100500)
'100500'
>>> str(1000000000000000000.0)
'1e+18'
>>> str(None)
'None'
>>> str('hello')
'hello'
>>> str(str)
"<class 'str'>"
Строки записываются в одинарных '...'
или двойных "..."
кавычках. В каких
кавычках записывать — это дело принятого стиля программирования. Чаще пишутся
одинарные кавычки.
Если строка записана в одинарных кавычках, то внутри можно использовать двойные:
'Он сказал: "Привет!"'
Если в двойных — внутри можно использовать одинарные (апострофы):
"Let's go, Brandon!"
Помимо обычных, печатных символов, существуют особые управляющие символы,
например, символ перевода строки. Для записи подобных символов используются
так называемые escape-последовательности — последовательности знаков,
начинающися со знака \
. (Клавиша «\» рядом с Backspace или «Ъ»
на клавиатуре, не путать с «/», расположенной между «Ю» и Shift!)
Escape-последовательность | значение |
---|---|
\\ |
Собственно, знак \ |
\n |
Перевод строки |
\r |
Возврат каретки (возврат к началу строчки) |
\t |
Знак табуляции (клавиша Tab) |
\xNN |
Символ с шестнадцатиричным кодом NN |
\uNNNN |
Символ с шестнадцатиричным кодом NNNN |
\UNNNNNNNN |
Символ с шестнадцатиричным кодом NNNNNNNN |
\b |
Возврат на символ назад при печати |
\a |
При печати выдаёт звуковой сигнал |
\" |
Двойная кавычка |
\' |
Одинарная кавычка (апостроф) |
Примеры на \r
и \b
(нужно запускать в окне консоли, в IDLE не работает):
>>> print('abcdefghi\r!!!')
!!!defghi
>>> print('hello\b!')
hell!
>>> print('hello\b\b\b!')
he!lo
>>> print('\a') # слышен системный звуковой сигнал
Другие примеры (работают в IDLE):
>>> print("abc\ndef\nghi")
abc
def
ghi
>>> print('\x30')
0
>>> print('\x40')
@
>>> print('\u0040')
@
>>> print('\u03BB')
λ
>>> print('\u2261')
≡
>>> print('7 \u2264 7, 8 \u2265 7')
7 ≤ 7, 8 ≥ 7
>>> print('Пример на табуляцию:\na\tbb\tccc\tdddd\teeeee\t!')
Пример на табуляцию:
a bb ccc dddd eeeee !
>>> print('1\t1\n22\t22\n333\t333\n4444\t4444\n55555\t55555')
1 1
22 22
333 333
4444 4444
55555 55555
Из всех escape-последовательностей чаще всего мы будем пользоваться переводом
строки (\n
), чуть реже табуляцией (\t
), представлением самого знака \
(\\
) и кавычками, чтобы их записывать внутри строк (\'
, \"
).
Символ перевода строки \n
при печати выполняет переход на новую строку.
Одна escape-последовательность представляет 1 символ строки. Например,
строка \n\n\n
состоит из трёх символов — трёх переводов строки.
К строкам применима операция +
, выполняющая конкатенацию (склеивание)
строк:
>>> "штука" + 'турка'
'штукатурка'
Для целых чисел из курса 1 класс известно, что 3×5 = 3+3+3+3+3 (5 раз). В Python это же применимо и к строкам:
>>> 'строка' * 3
'строкастрокастрока'
>>> 20 * '*'
'********************'
Можно использовать сокращённые операторы присваивания:
>>> s = 'штука'
>>> s += 'турка'
>>> s
'штукатурка'
Строки можно сравнивать при помощи операций отношения, сравнение осуществляется в алфавитном порядке (точнее, по номерам символов в таблице Юникода):
>>> 'арбуз' < 'дыня'
True
>>> 'штукатурка' > 'штука'
True
>>> 'hello' <= 'bye'
False
>>> 'адвокат' > 'антрекот'
False
>>> 'адвокат' < 'антрекот'
True
Если одна строка является префиксом другой, то более короткая строка считается меньше.
Для строк есть ещё одна операция отношения — in
, проверяет вхождение подстроки
>>> 'кот' in 'антрекот'
True
>>> 'пёс' in 'антрекот'
False
>>> 'пёс' not in 'антрекот'
True
>>> 'ад' not in 'адвокат'
False
Операция not in
противоположна операции in
.
К строкам применима встроенная функция len(•)
, возвращающая длину строки:
>>> len('штукатурка')
10
>>> len('')
0
>>> len('\\\\')
2
>>> len('\n\n\n')
3
Можно обращаться символу в строке по номеру, используя операцию
индексации s[i]
, где s
— строка, i
— индекс или номер символа. Индекс
может быть положительным от 0
до len(s)-1
, где len(s)
— длина строки
(символы строки нумеруются с нуля).
Либо отрицательным от -len(s)
до -1
, в этом случае имеется ввиду номер
от конца строки. Если индекс отрицательный, например s[-x]
, то подразумевается
индекс s[len(s)-x]
. Т.е. чтобы из отрицательного индекса получить настоящий,
нужно его прибавить к длине строки.
>>> s = 'Hello!'
>>> s[0]
'H'
>>> s[1]
'e'
>>> s[2]
'l'
>>> s[5] # это последний, т.к. индекс должен быть
'!' # от 0 до len(s)−1
>>> len(s)
6
>>> s[-1] # обращение к последнему
'!'
>>> s[-2] # предпоследний
'o'
>>> s[-6] # начальный
'H'
Строки, как и другие типы данных Python, являются объектами, и у них есть свои методы. Все методы строк можно получить во встроенной справке:
>>> help(str)
Некоторые полезные методы:
s.split()
— разбивает строку по пробельным символам,s.split(sep)
— разбивает строку по указанному разделителю,s.join(xs)
— объединяет элементы последовательности xs
с разделителем s
,
xs
— должна быть последовательностью строк,s.startswith(t)
, s.endswith(t)
— True
, если строка s
начинается или
заканчивается на t
,s.find(t)
— ищет индекс, начиная с которого подстрока t
входит в строку
s
, -1
, если не найдено.s.trim()
— удаляет из строки начальные и конечные пробельные символы
(пробелы, табуляции, \n
, \r
),True
, когда строка непустая
и все символы заданного типа:
s.isdigit()
— все цифры,s.isalpha()
— все буквы,s.isalnum()
— все либо буквы, либо цифры,s.isspace()
— все пробельные символы (пробел, \t
, \n
, \r
),s.ispunct()
— все знаки пунктуации и т.д.Примеры:
>>> 'раз два три четыре'.split()
['раз', 'два', 'три', 'четыре']
>>> 'one,two,,three'.split(',')
['one', 'two', '', 'three']
>>> '+'.join(['раз', 'два', 'три'])
'раз+два+три'
>>> '+'.join('hello')
'h+e+l+l+o'
>>> ' \n\t строка \t\t\n\r\n '.trim()
'строка'
>>> 'штукатурка'.startswith('штука')
True
>>> 'штукатурка'.startswith('привет')
False
>>> 'штукатурка'.endswith('турка')
True
>>> 'штукатурка'.endswith('грек')
False
>>> 'раз два три'.find('раз')
0
>>> 'раз два три'.find('два')
4
>>> 'раз два три'.find('четыре')
-1
>>> ' Привет, штукатурка! '.trim()
'Привет, штукатурка!'
>>> '\n\n\nПривет, штукатурка!\n\n\n'.trim()
'Привет, штукатурка!'
>>> ' Привет, штукатурка! '.ltrim()
'Привет, штукатурка! '
>>> ' Привет, штукатурка! '.rtrim()
' Привет, штукатурка!'
>>> 'привет'.isalpha()
True
>>> 'привет!'.isalpha()
False
>>> ''.isalpha()
False
>>> '1'.isdigit()
True
>>> 'один'.isdigit()
False
>>> 'привет'.isalnum()
True
>>> '100500'.isalnum()
True
>>> 'Агент007'.isalnum()
True
>>> 'B-52'.isalnum()
False
>>> ' '.isspace()
True
>>> '\n\n\n'.isspace()
True
>>> '\t\t\t'.isspace()
True
>>> ' !'.isspace()
False
chr(n)
и ord(n)
Юникод — международный стандарт на кодирование символов. В таблицу Юникода
входят символы различных алфавитов, знаки пунктуации, математические символы,
управляющие символы (\n
, \t
, \r
, \b
…), смайлики (эмодзи) и так далее.
В таблице Юникода каждому символу сопоставляется его номер, т.н. кодовая
точка. Всего в Юникоде возможно 1 114 112 (2**20 + 2**16
), многие из кодовых
точек до сих пор не распределены.
Функция chr(n)
возвращает символ с кодовой точкой n
:
>>> chr(48)
'0'
>>> chr(0x3bb)
'λ'
>>> 0x3bb
955
>>> chr(955)
'λ'
Функция ord(c)
возвращает кодовую точку символа c
:
>>> ord('@')
64
>>> ord('λ')
955
>>> ord('Ы')
1067
>>> ord(' ')
32
KeyboardInterrupt
>>> ord('😊')
128522
>>> chr(128522)
'😊'
UTF-8 — один из способов кодирования символов Юникода в виде последовательности байтов. При данном способе кодирования различным символам Юникода назначаются последовательности длиной от 1 до 4 байтов. Символы, входящие в таблицу ASCII (латинские буквы, десятичные цифры, основные знаки пуктуации) кодируются 1 байтом, греческие и кириллические буквы — двумя, иероглифы и смайлики — 3–4 байтами в зависимости от кода.
bool
)Мы о нём уже говорили, когда обсуждали логические выражения, условный оператор
и цикл с предусловием (while
). Конструктор булевского типа — функция bool
,
принимает любое значение и трактует его как истину или ложь.
>>> bool(100)
True
>>> bool(0)
False
>>> bool('Непустая строчка')
True
>>> bool('')
False
>>> bool(None)
False
Операции с логическим типом and
, or
, not
мы подробно рассматривали ранее.
Функция abs(x)
, которая возвращает абсолютное значение числа, как ни странно,
применима и для логических значений. Возвращает 1
для истины, 0
для лжи:
>>> abs(True)
1
>>> abs(False)
0
Литералы типа bool
: True
и False
.
list
)Список — изменяемый контейнер значений, индексируемый целыми числами
в диапазоне от 0
до некоторой величины — длины списка.
Литерал списка — элементы списка, перечисленные через запятую в квадратных скобках
Пример:
[2, 3, 5, 7, 11, 13, 17]
Это список из 7 значений, которые являются первыми 7 простыми числами.
Конструктор списка — функция list
, которая принимает любую итерируемую
последовательность и формирует список с этими элементами:
>>> list('Привет!')
['П', 'р', 'и', 'в', 'е', 'т', '!']
>>> list([5, 7, 7])
[5, 7, 7]
>>> list(range(10))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Длину списка можно узнать при помощи встроенной функции len(xs)
:
>>> len([5, 7, 7, 8])
4
Операция индексирования. К элементам списка можно обращаться по номеру,
используя квадратные скобки. Нумерация может быть как положительной — от 0
до len(‹список›)−1
, так и отрицательной — от −1
до −len(‹список›)
.
Положительный индекс позволяет обратиться к элементу, начиная с начала,
отрицательный — начиная с конца (xs[-1]
— последний элемент, xs[-len(xs)]
—
первый элемент). Т.е. тут аналогично со строками:
>>> xs = [2, 3, 5, 7, 11]
>>> xs[0]
2
>>> xs[4]
11
>>> xs[-1]
11
>>> xs[-5]
2
>>> xs[3]
7
Но, в отличие от строк, список является изменяемой структурой данных. Обращение к элементу списка может располагаться слева в операторе присваивания:
>>> xs[2] = 10
>>> xs
[2, 3, 10, 7, 11]
>>> xs[-1] *= 5
>>> xs
[2, 3, 10, 7, 55]
>>> xs[0] = 0
>>> xs
[0, 3, 10, 7, 55]
«Сложение и умножение». Тут тоже похоже на строки: операция +
позволяет
склеивать два списка:
>>> [1, 2, 3] + [4, 5, 6]
[1, 2, 3, 4, 5, 6]
Операция *
, применённая к списку и целому числу, конкатенирует (склеивает)
список с собой указанное количество раз:
>>> [1, 2] * 5
[1, 2, 1, 2, 1, 2, 1, 2, 1, 2]
>>> ys = [0] * 10
>>> ys
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
В примере с ys
мы создали список из нулей длины 10. Вообще, это распространённый
приём. Если в задаче требуется иметь список чисел заданной длины, заполненный
изначально нулями, то просто умножаем список из одного нуля [0]
на заданную
длину: [0] * n
.
К спискам применимы и сокращённые операторы присваивания:
>>> a = [2, 3, 4]
>>> a += [5, 7, 8]
>>> a
[2, 3, 4, 5, 7, 8]
>>> b = [1, 2]
>>> b *= 3
[1, 2, 1, 2, 1, 2]
Операция in
позволяет узнать, есть ли в списке данное значение:
>>> 5 in [2, 3, 5, 7]
True
>>> 10 in [2, 3, 5, 7]
False
>>> 10 not in [2, 3, 5, 7]
True
Самый распространённый метод — xs.append(x)
— добавляет элемент в конец
списка.
>>> xs = []
>>> xs.append(10)
>>> xs.append('hello')
>>> xs.append([])
>>> xs.append(True)
>>> xs
[10, 'hello', [], True]
Метод xs.sort()
сортирует список на месте. Список можно отсортировать,
если к его элементам применимы отношения >
, <
, >=
и т.д., в противном
случае получим ошибку.
>>> xs = [12, 54, 15, 11, 20, 21]
>>> xs.sort()
>>> xs
[11, 12, 15, 20, 21, 54]
Список сортируется на месте, т.е. его содержимое изменяется.
Метод xs.count(x)
возвращает количество элементов с заданным значением:
>>> xs = [1, 2, 5, 7, 1, 5, 1, 1, 2, 0, 2, 1]
>>> xs.count(1)
5
На самом деле, многие из рассмотренных функций могут работать не только со списками, но и с любыми другими итерируемыми последовательностями, но об этом поговорим позже.
Функция max(xs)
возвращает наибольшее значение в списке. Список должен
быть не пустым. min(xs)
— соответственно, наименьшее значение.
>>> max([13, 1, 15, 11, 20, 21])
21
>>> min([13, 1, 15, 11, 20, 21])
1
Функция sorted(xs)
возвращает новый список со значениями, отсортированными
по возрастанию. Исходный список не меняется.
>>> xs = [12, 54, 15, 11, 20, 21]
>>> ys = sorted(xs)
>>> xs
[12, 54, 15, 11, 20, 21]
>>> ys
[11, 12, 20, 21, 54]
Функция reversed(xs)
строит итерируемую последовательность элементов списка
в обратном порядке. Чтобы её сделать списком, её нужно передать конструктору
list
:
>>> rs = reversed([12, 54, 15, 11, 20, 21])
>>> rs
<list_reverseiterator object at 0x000001602264DE80>
>>> ys = list(rs)
>>> ys
[21, 20, 11, 15, 54, 12]
tuple
)Кортеж — неизменяемый контейнер с элементами, нумеруемыми последовательными целыми числами. Используется для группировки нескольких значений в одно.
Конструктор типа — функция tuple
, работает аналогично функции list
.
Запись кортежей — перечисление элементов через запятую в круглых скобках, иногда снаружи круглые скобки можно опустить:
>>> xs = (2, 3, 5, 7, 11)
>>> xs
(2, 3, 5, 7, 11)
>>> ys = 2, 3, 5, 7, 11
>>> ys
(2, 3, 5, 7, 11)
>>> xs == ys
True
Чтобы создать кортеж из одного элемента, нужно после него добавить запятую:
>>> number_one = (1)
>>> number_one
1
>>> tuple_with_one = (1,)
>>> tuple_with_one
(1,)
>>> other_tuple_with_one = 1,
>>> other_tuple_with_one
(1,)
Запятая после 1 означает, что это кортеж, а не просто число.
Кортеж можно считать аналогом списка, содержимое которого запрещено изменять:
>>> xs = [1, 2, 3]
>>> xs[0]
1
>>> xs[0] = 10
>>> xs
[10, 2, 3]
>>> ys = (1, 2, 3)
>>> ys[0]
1
>>> ys[0] = 10
‹СООБЩЕНИЕ ОБ ОШИБКЕ›
В программе, как правило, списки используют для представления набора однородных данных (список чисел, список строк, список каких-то объектов) неизвестной длины (длина определяется входными данными), который можно менять. Кортежи используются для представления разнородных данных, они небольшого размера и длина известна программисту на момент написания программы.
Если длина списка или кортежа известна, то его содержимое можно распаковать оператором присваивания. Для этого слева нужно перечислить через запятую столько переменных, сколько элементов в самом контейнере:
>>> xs = [2, 3, 4]
>>> a, b, c = xs
>>> a
2
>>> b
3
>>> c
4
>>> ys = 7, 5, 9, 8
>>> a, b, c, d = ys
>>> c
9
Такой приём удобен, если список или кортеж небольшой. Если контейнер справа в операторе присваивания большой, то при помощи распаковки можно выделить несколько значений в начале, в конце, а всё, что между ними — поместить в новый список. Перед переменной, куда будет помещён список, нужно поставить звёздочку:
>>> xs = [1, 2, 3, 4, 5, 6, 7]
>>> a, b, *ys, c, d = xs
>>> a
1
>>> b
2
>>> c
6
>>> d
7
>>> ys
[3, 4, 5]
Переменная со звёздочкой может быть только одна, в эту переменную всегда попадает список, даже если справа находится не список:
>>> xs = 1, 2, 3, 4
>>> *ys, z = xs
>>> ys
[1, 2, 3]
>>> z
4
Распаковка контейнеров позволяет элегантно обменивать значения двух переменных:
>>> a = 7
>>> b = 8
>>> a, b = b, a
>>> a
8
>>> b
7
Справа в операторе присваивания строится кортеж (b, a)
(скобки тут опущены),
который тут же распаковывается.
В Python, в отличие от языков типа C++ или Pascal, нет параметров-ссылок, т.е. нет возможности из функции изменить значение переменной, которая передана как параметр:
def some_function(‹какие-то параметры›):
...
a = 100
b = [1, 2, 3]
other_function(a, b)
...
После вызова other_function
переменные a
и b
будут иметь те же значения,
что и до вызова — в a
будет лежать число 100
, в b
— ссылка на тот же список
(хотя содержимое списка может измениться). В Python нет возможности изменить
значения переменных из вызванной функции.
Поэтому поступить как в Паскале или C++ — передать параметр по ссылке и изменить его внутри функции, мы не можем.
Единственный способ передать из вызова функции наружу — вернуть значение при
помощи return
.
Если мы хотим передать из функции несколько значений — нам нужно упаковать их в какой-нибудь контейнер и вернуть контейнер. В качестве такого контейнера чаще всего используют кортеж.
Если в операторе return
перечислить через запятую несколько значений, функция
вернёт кортеж из этих значений:
return a, b, c
Для анализа этих значений можно использовать распаковку
def divmod(a, b):
return a // b, a % b
Функция divmod(a, b)
выполняет деление с остатком: возвращает частное и остаток.
>>> div, rem = divmod(30, 4)
>>> div
7
>>> rem
2
Ранее мы говорили, что операция индексирования xs[i]
позволяет обратиться
к одному элементу строки, списка или кортежа. В случае списков, операция
индексирования может располагаться в левой части оператора присваивания:
xs[5] = 6
поскольку список является изменяемым типом данных.
Однако, при помощи операции индексирования можно обращаться и целым участкам последовательностей, т.е. списков, строк или кортежей. Это так называемый синтаксис срезов:
xs[‹start›:‹limit›:‹step›]
где
‹start›
— индекс первого элемента среза,‹limit›
— индекс граничного элемента среза, т.е. участок от ‹start›
до ‹limit›
включает в себя ‹start›
и не включает в себя ‹limit›
.
Пользуясь математическими обозначениями, границы можно записать как
полуоткрытый интервал [‹start›, ‹limit›)
.‹step›
— шаг, с которым выбираются элементы.Каждый из этих компонентов может отсутствовать, в этом случае подразумевается:
‹start›
— подразумевается от начала последовательности,‹limit›
— до конца последовательности, т.е. до len(xs)
,‹step›
— шаг равен 1, т.е. элементы перечисляются без
пропусков.Например:
0 1 2 3 4 5 6 7
>>> xs = [2, 3, 5, 7, 11, 13, 17, 19]
-8 -7 -6 -5 -4 -3 -2 -1
>>> len(xs)
8
>>> xs[3:6]
[7, 11, 13]
>>> xs[2:-2]
[5, 7, 11, 13]
>>> xs[:4]
[2, 3, 5, 7]
>>> xs[4:]
[11, 13, 17, 19]
>>> xs[3:6:2]
[7, 11]
>>> xs[::3]
[2, 7, 17]
Шаг может быть отрицательным — в этом случае элементы перечисляются в обратном
порядке. В этом случае ‹start›
должен быть больше, чем ‹limit›
:
>>> xs[::-3]
[19, 11, 3]
>>> xs[1:6:-2]
[]
>>> xs[6:1:-2]
[17, 11, 5]
На практике чаще всего используют отрицательный шаг -1
для того, чтобы
перевернуть список, кортеж или строку:
>>> xs[::-1]
[19, 17, 13, 11, 7, 5, 3, 2]
>>> s = 'hello'
>>> s[::-1]
'olleh'
>>> "Madam, I'm Adam"[::-1]
"mada m'I madaM"
>>> "Eve"[::-1]
'evE'
Списки являются изменяемой структурой данных, с ними можно использовать присваивания срезам:
>>> xs
[2, 3, 5, 7, 11, 13, 17, 19]
>>> xs[3:6]
[7, 11, 13]
>>> xs[3:6] = [33, 44, 55]
>>> xs
[2, 3, 5, 33, 44, 55, 17, 19]
>>> xs[2:4] = [111, 222, 333]
>>> xs
[2, 3, 111, 222, 333, 44, 55, 17, 19]
>>> xs[-4:-1] = []
[2, 3, 111, 222, 333, 19]
А что означает срез xs[:]
? Очевидно, это всё содержимое списка от начала
и до конца. Зачем оно нужно? На практике такой срез используется, чтобы
получить копию списка.
Дело в том, что список — ссылочный тип данных. Когда мы присваиваем список другой переменной, то у нас копируется только ссылка — обе переменные ссылаются на один и тот же список. Можно сказать, что у одного списка получаются два равноправных псевдонима.
И если теперь поменять значение списка через одну переменную, мы это же изменение увидим через другую:
>>> xs = [2, 3, 5, 7, 11]
>>> ys = xs # Здесь мы скопировали ссылку
>>> ys
[2, 3, 5, 7, 11]
>>> xs[2] = 88
>>> ys[2]
88
>>> xs
[2, 3, 88, 7, 11]
>>> ys
[2, 3, 88, 7, 11]
>>> ys.append(100500)
>>> ys
[2, 3, 88, 7, 11, 100500]
>>> xs
[2, 3, 88, 7, 11, 100500]
Переменные xs
и ys
в примере — два псевдонима для одного объека списка.
В этом примере в памяти компьютера находится один список, на который ссылаются
переменные xs
и ys
, можем видеть и менять его содержимое через любую из них.
>>> xs = [2, 3, 5, 7, 11]
>>> zs = xs[:] # Здесь мы сделали срез [:]!!!
>>> zs
[2, 3, 5, 7, 11]
>>> xs[2] = 88
>>> zs[2]
5
>>> xs
[2, 3, 88, 7, 11]
>>> zs
[2, 3, 5, 7, 11]
>>> zs.append(100500)
>>> zs
[2, 3, 5, 7, 11, 100500]
>>> xs
[2, 3, 88, 7, 11]
Во втором примере мы при помощи среза xs[:]
создали новый список с тем же
содержимым. Т.е. в памяти компьютера образовалось два равных списка, на один
ссылается переменная xs
, на другой — zs
. Через переменную xs
мы можем
видеть и менять содержимое первого списка, через zs
— второго. Изменения
обоих списков независимы.
Срез xs[:]
может использоваться для изменения содержимого всего списка.
>>> xs = [2, 3, 5, 7]
>>> ys = xs
>>> ys = [11, 12, 13]
>>> xs
[2, 3, 5, 7]
Когда мы присвоили переменной ys
новый список, мы просто в неё положили
ссылку на новый список. Содержимое списка xs
мы не меняли.
>>> xs = [2, 3, 5, 7]
>>> ys = xs
>>> ys[:] = [11, 12, 13] # Присваиваем срезу ys[:]!!!
>>> xs
[11, 12, 13]
Тут мы поменяли содержимое списка, на который ссылаются две переменные xs
и ys
через переменную ys
. Соответственно, т.к. эти переменные — псевдонимы,
через xs
видно изменение.
В Python все данные — ссылочные. Но для неизменяемых типов (int
, float
,
str
, tuple
) это не важно, т.к. для них копирование ссылки эквивалентно
копированию значения.
Для изменяемых типов данных (list
, dict
, set
) о том, что данные ссылочные,
помнить нужно. Присваивание переменной не создаёт новый список (или словарь,
или множество), а просто копирует ссылку на объект, создаёт псевдоним.
Изменения, сделанные через один псевдоним, будут видны и через другой. Чтобы
создать новый объект и менять его независимо, нужно его явно скопировать.
Например, для списка при помощи среза xs[:]
.
Частный случай присваивания — присваивания параметрам при вызове функции:
def func(xs):
xs[0] = 1
При вызове этой функции содержимое списка будет меняться:
>>> ys = [5, 7, 9]
>>> func(ys)
>>> ys
[1, 7, 9]
При вызове функции параметру xs
была присвоена ссылка на тот же список,
что и в переменной ys
— а значит, функция изменила чужой список.
>>> zs = [5, 7, 9]
>>> func(zs[:])
>>> zs
[5, 7, 9]
Тут мы хитрые, передали в функцию копию списка, функция поменяла содержимое копии, исходный список остался неприкосновенным.
dict
)Ранее мы рассматривали последовательности — контейнеры, элементы в которых сопоставлялись последовательным целым числам: от 0 до N−1 или (если ссылаться справа налево) от −1 до −N. К элементам последовательности мы обращаемся по номеру.
Словарь (dict
) — контейнер, к элементам которого можно обращаться
по ключам, например, строкам. Словарь хранит внутри себя пары значений
в произвольном порядке. Первый элемент каждой пары называется ключом,
второй — значением. Каждому ключу сопоставляется ровно одно значение —
ключи повторяться не могут. А значения повторяться могут.
Словарь позволяет находить или изменять значение (второй компонент пары), зная её ключ. Синтаксис тот же, что и при индексации массива:
>>> exam_scores['Вася'] = 5
>>> exam_scores['Маша']
4
Т.е. в словаре exam_scores
(экзаменационные оценки) с ключом 'Вася'
мы
связали значение 5
, ранее с другим ключом 'Маша'
было связано значение 4
.
Можно сказать, что ключ — это «имя» для некоторого значения.
Конструктор словаря — функция dict()
. Без параметров создаёт пустой словарь,
её параметром может быть список пар.
>>> dict()
{}
>>> dict( [('Вася', 5), ('Маша', 4), ('Коля', 4), ('Лена', 5)] )
{'Вася':5, 'Маша':4, 'Коля':4, 'Лена':5}
Создать словарь можно, перечислив его содержимое в фигурных скобках. Содержимое
записывается как пары ‹ключ› : ‹значение›
, перечисленные через запятую. В каждой
паре ключ и значения разделяются двоеточием:
>>> d = { 'Саша':3, 'Кеша':2 }
>>> d
{'Саша':3, 'Кеша':2}
Ключами словаря могут служить только неизменяемые значения:
int
, float
,str
,tuple
) из других неизменяемых значений.Обращаться к элементам словаря можно при помощи операции индексации, примеры на которую мы рассмотрели выше:
>>> exam_scores['Вася'] = 5
>>> exam_scores['Маша']
4
Объединять значения двух словарей можно при помощи операции |
: d1 | d2
,
при этом значения из d2
имеют приорирет, т.е. если в обоих словарях есть
одинаковые ключи, то значения для них берутся из второго словаря:
>>> d1 = {'a' : 5, 'b' : 7, 'c' : 8 }
>>> d2 = {'d' : 4, 'f' : 1, 'c' : 0 }
>>> d12 = d1 | d2
>>> d12
{'a':5, 'b':7, 'c':0, 'd':4, 'f':1}
>>> d2 | d1
{'a':5, 'b':7, 'c':8, 'd':4, 'f':1}
Операция del
позволяет удалить значение из словаря:
>>> d1 = {'a' : 5, 'b' : 7, 'c' : 8 }
>>> del d1['b']
>>> d1
{'a':5, 'c':8}
Метод dict.get()
извлекает значение из словаря:
d.get(key)
d.get(key, default)
Операция индексации при обращении к несуществующему ключу приводит к ошибке (как и при выходе за границы списка).
>>> d = {'a':5, 'b':8, 'c':10}
>>> d['a']
5
>>> d['d']
‹СООБЩЕНИЕ ОБ ОШИБКЕ›
Метод get
к ошибке не приводит. Если ключа не существует, то возвращается
значение по умолчанию default
, если default
не указан, то подразумевается
None
:
>>> d.get('d')
>>> print(d.get('d'))
None
>>> d.get('d') == None
True
>>> d.get('a')
5
>>> d.get('a', 100500)
5
>>> d.get('d', 100500)
100500
При помощи метода get
можно представлять себе словарь как бесконечный,
при этом всем реально не существующим ключам get
сопоставляет значение
по умолчанию.
Например, если мы посчитываем какие-то значения (например, слова), то можем
считать, что для несуществующих слов их количество равно нулю, вызывая
words.get(word, 0)
.
Пример. Напишем функцию dict_get
, имитирующую поведение метода get
:
def dict_get(d, key, default):
d_ext = {key : default} | d
return d_ext[key]
В словаре d_ext
заведомо будет нужный ключ, т.к. он получен объединением
словаря {key : default}
с исходным. При этом в операции объединения |
исходный словарь указан справа, поэтому если в нём есть искомое значение,
то оно перекроет значение default
из словаря слева. Если нет, в d_ext
останется default
для ключа key
.
Метод items
возвращает список (на самом деле, итератор, но о них позже)
с парами (‹ключ›, ‹значение›)
.
>>> d = {'a':5, 'b':8, 'c':10}
>>> list(d.items())
[('a', 5), ('b', 8), ('c', 10)]
Метод items
удобно использовать в цикле for
(об этом на следующей лекции).
Метод copy
создаёт копию словаря (похож на срез xs[:]
для списков).
set
)Тип данных «множество» (set
) хранит неупорядоченный набор значений без
повторений. Элементами множества могут быть только неизменяемые типы
данных (как и ключи в словаре): числа, строки, кортежи из чисел и строк.
В отличие от списка, данные во множестве неупорядочены — при переборе его содержимого элементы будут просматриваться в произвольном порядке. Т.е. как множество захочет расположить внутри себя элементы, так и расположит, мы на это повлиять никак не можем.
Элементы во множестве не повторяются — если мы попробуем добавить ко множеству элемент, который там присутствует, то множество просто не изменится.
Конструктор множества — set
, может быть вызван без параметров,
тогда будет создано пустое множество, может получать на входе некоторый
другой контейнер (например, список или строку) — будет построено
множество из элементов этого контейнера.
>>> empty = set() # создание пустого множества
>>> empty
set()
>>> hello = set('hello')
>>> hello # в реальности они могут быть
{ 'h', 'e', 'l', 'o' } # показаны в любом порядке
Можно создать множество путём перечисления элементов в фигурных скобках:
>>> primes = { 2, 3, 5, 7, 11 }
>>> primes
{ 2, 3, 5, 7, 11 }
В Python фигурные скобки используются в двух ролях: для создания словарей и создания множеств. Разница между ними в том, что при создании словарей указываются ключи:
>>> xs = { 'Вася': 5, 'Петя': 4 }
>>> ys = { 'Вася', 'Петя' }
В переменной xs
будет находиться словарь, в переменной ys
будет
находиться множество. Поскольку мы отличаем создание словаря от создания
множества по содержимому фигурных скобок, то пустые фигурные скобки {}
могут трактоваться неоднозначно: и как пустой словарь, и как пустое
множество.
Поскольку на практике словари используются чаще множеств, разработчики
Python решили, что пустые фигурные скобки {}
будут представлять пустой
словарь. А для создания пустого множества нужно вызывать конструктор set()
без параметров.
Операция in
(not in
) проверяет входит ли (не входит ли) элемент
во множество:
>>> primes = { 2, 3, 5, 7, 11 }
>>> 5 in primes
True
>>> 6 in primes
False
>>> 6 not in primes
True
Операция |
, как и для словарей, строит объединение множеств:
>>> xs = { 2, 4, 6, 8 }
>>> ys = { 2, 3, 5, 7 }
>>> xs | ys
{ 2, 3, 4, 5, 6, 7, 8 }
Операция &
строит пересечение множеств:
>>> xs & ys
{ 2 }
Операция -
вычисляет разность множеств:
>>> xs - ys
{ 3, 5, 7 }
>>> ys - xs
{ 4, 6, 8 }
Для этих операций есть соответствующие сокращёные записи оператора
присваивания: |=
, &=
, -=
.
Самый основной метод — метод .add(x)
, добавляющий элемент во множество:
>>> s = set()
>>> s.add(2)
>>> s.add(3)
>>> s
{ 2, 3 }
>>> s.add(2)
>>> s
{ 2, 3 }
Метод .remove(x)
удаляет элемент из множества. Если его вызвать
к отсутствующему элементу, получим ошибку. Для того, чтобы удалить элемент,
но при этом не получить ошибку, если он отсутствовал, нужно просто вычесть
множество из одного элемента:
>>> s.remove(3)
>>> s
{ 2 }
>>> s.remove(3)
‹сообщение об ошибке KeyError›
>>> s -= { 2 }
>>> s
set()
>>> s -= { 2 }
>>> s
set()
Дан список элементов. Составить новый список, в котором отстутсвуют дубликаты, причём порядок не важен. Для этого мы из списка делаем множество, а из него — снова список:
>>> xs = [ 3, 7, 8, 2, 3, 4 ]
>>> ys = list(set(xs))
>>> ys
[ 3, 4, 2, 8, 7 ]
Дан список элементов. Проверить — есть ли в нём дубликаты?
def has_repeated(xs):
return len(xs) > len(set(xs))
Почему работает эта функция? Потому что если в списке будут дубликаты, то множество, построенное из него, будет содержать меньше элементов, чем в исходном списке.