Последовательности: tuple, str
Contents
Последовательности: tuple, str¶
И снова про типы данных.
Следующие контейнеры являются последовательностями (Sequance Types
):
list*
- списки;tuple
- кортежи;range
;str
- строки;bytes
,bytearray*
- бинарные данныеarray*
и некоторые другие;
Типы помеченные символом *
— изменяемые, остальные нет.
Все последовательности (Sequance Types
) упорядочены (пронумерованы). Это означает возможна их индексация по смещению (zero-based
).
Операция |
Описание |
---|---|
|
|
|
Наоборот. |
|
Конкатенация |
|
Эквивалентно добавлению |
|
|
|
Срез |
|
Срез |
|
Длинна |
|
Наименьший элемент в |
|
Наибольший элемент в |
|
Индекс первого совпадения |
|
Количество раз |
Списки. (list)¶
Под капотом списки реализованы в виде динамического массива (похожего на std::vector
) указателей на его элементы.
Создать список можно несколькими способами:
Парой квадратных скобок, чтобы обозначить пустой список:
[]
;Используя пару квадратных скобок и разделяя элементы списка запятыми:
[a]
,[a, b, c]
;Используя конструктор типа:
list()
илиlist(iterable)
;Используя списковые включения (
list comprehensions
):[x for x in iterable]
List comprehensions — очень мощный инструмент для работы со списками и будет обсуждаться позже.
Создание списков с помощью умножения¶
Выражение [x] * n
может быть использовано для того, чтобы создавать не пустой список, а список из n
одинаковых элементов x
. В качестве примера создадим нулевой n
мерный вектор.
v = [0] * 10
print(v)
v[0] = 1
print(v)
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0]
Но необходимо проявлять бдительность. Выражение x
вычисляется один раз, что может привести к неожиданному поведению, если x
изменяемого типа. Попробуем создать нулевую матрицу из 3
строк и 4
столбцов и поменять один элемент. Будем хранить матрицу в виде списка, элементами которого являются строки матрицы. Выражение [0] * 4
создаст одну нулевую строку матрицы. Может показаться, что выражение [[0] * 4] * 3
создаст нулевую матрицу из трех таких строк.
def print_matrix(matrix):
print("-" * 12) # "-" * 12 работает так же и для строки, но результат строка
for row in matrix:
print(row)
print("-" * 12)
m = [[0] * 4] * 3
print_matrix(m)
m[0][0] = 1
print_matrix(m)
------------
[0, 0, 0, 0]
[0, 0, 0, 0]
[0, 0, 0, 0]
------------
------------
[1, 0, 0, 0]
[1, 0, 0, 0]
[1, 0, 0, 0]
------------
Результат объясняется тем, что выражение [0] * 4
было вычислено единожды и создало один список [0, 0, 0, 0]
, на который ссылаются все указатели списка m
.
Методы списка.¶
Операция |
Описание |
---|---|
|
Изменяет |
|
Срез с |
|
То же самое, что и |
|
Элементы среза с |
|
Удаляет элементы этого среза из списка. |
|
Добавляет элемент в конец (аналог |
|
Удаляет все элементы из списка. Эквивалентно |
|
Создаёт копию списка. Эквивалентно |
|
Расширяет список, добавляя все элементы итерируемого объекта в конце списка. |
|
То же, что и |
|
Вставляет элемент по заданной позиции. Первый аргумент — индекс элемента, перед которым нужно вставить элемент (второй аргумент). |
|
|
|
Удаляет первый элемент списка, который равен |
|
Обращает список на месте. |
|
Сортирует список на месте. |
Note
Все эти методы доступны также и для других изменяемых последовательностей (mutable sequence types
), кроме метода l.sort()
.
Note
В python методы, изменяющие объекты на месте, обычно ничего не возвращают. Например, в итоге вычисления выражения l = l.sort()
имя l
будет указывать на None
.
Кортежи (tuple)¶
Кортежи — аналог списков, но неизменяемый (immutable
).
Создать кортеж можно несколькими способами:
Парой круглых скобок, чтобы обозначить пустой кортеж:
()
;Используя хвостовую запятую с или без круглых скобок, чтобы обозначить кортеж из одного элемента:
a,
или(a, )
;Разделяя элементы списка запятыми:
a, b, c
илиb, c
;Используя конструктор типа:
tuple()
илиtuple(iterable)
;
empty_tuple = () # или tuple()
singleton_tuple = 0, # или singleton_tuple = (0, )
an_int = (0) # но не так, это int
a_tuple = 1, 2, 3 # или a_tuple = (1, 2, 3)
a_list = ["a", "b", "c"]
a_tuple_from_list = tuple(a_list)
print(empty_tuple, singleton_tuple, an_int, a_tuple, a_list, a_tuple_from_list, sep="\n")
()
(0,)
0
(1, 2, 3)
['a', 'b', 'c']
('a', 'b', 'c')
Неизменяемость кортежей¶
Кортеж под капотом является константным массивом указателей на его элементы. В отличие от списка ни размер, ни содержимое этого массива менять нельзя.
a_tuple = 1, 2, 3
a_tuple[0] = 42 # ошибка
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
~\AppData\Local\Temp/ipykernel_12476/1536374831.py in <module>
1 a_tuple = 1, 2, 3
----> 2 a_tuple[0] = 42 # ошибка
TypeError: 'tuple' object does not support item assignment
Попытка изменить содержимое кортежа вызвало ошибку. Более точно, попытка изменить ссылку, на которую ссылается кортеж вызвало ошибку. Если объект по этой ссылке изменяемый (mutable
), то изменить этот объект можно.
a_tuple_with_list = [],
a_tuple_with_list[0].append("Работает!")
Т.е. кортеж не даёт гарантии, что его элементы не изменятся, а гарантирует лишь то, что его элементы останутся теми же самыми объектами.
Кортежи vs списки¶
После всего, что мы узнали может возникнуть вопрос: в чем смысл использовать кортежи, если у них урезанный функционал (по сравнению со списками), а их отличительная особенность (неизменяемость) не гарантирует неизменность его элементов?
У кортежей есть два основных преимущества на фоне списков, оба из которых обусловлены их неизменяемостью:
Они работают быстрее;
От них можно вычислять
hash()
. Будет обсуждаться позже.
И списки и кортежи позволяют хранить произвольное количество элементов произвольного типа, но обычно стараются следовать следующему правилу.
Списки используют для хранения однородных объектов.
Кортежи используют для хранения разнородных объектов.
Распаковка кортежей¶
Очень часто кортежи используются для того, чтобы упаковать несколько объектов, передать их как единое целое и затем распаковать эти объекты обратно.
a_tuple = (42, 321.0, "a string")
first, second, third = a_tuple
print(first, second, third)
42 321.0 a string
Это позволяет очень выразительно писать функции, как бы возвращающие несколько значений. В качестве примера напишем функцию, которая возвращает сразу и минимальное и максимальное значение списка.
def min_and_max(array):
if not array:
return None
min_value, max_value = array[0], array[0] # Объявление нескольких имен
for value in array:
min_value = min(min_value, value)
max_value = max(max_value, value)
return min_value, max_value # Тут создаётся кортеж из двух элементов
a_list = [1, 50, 100, 200]
m, M = min_and_max(a_list) # А тут он распаковывается в переменные m и M
# после выполнения функции min_and_max
print(m, M)
1 200
Примером встроенной функции, возвращающей 2 аргумента, является divmod()
.
d, m = divmod(11, 3)
print(d, 11 // 3)
print(m, 11 % 3)
3 3
2 2
Note
Распаковывать можно и списки, но это кроме того, что вынуждает писать дополнительные скобочки, ещё и работает медленнее.
Строки¶
Строки — неизменяемые последовательности (immutable sequence type
), предназначенные для работы с текстом в Unicode. Можно считать, что это массив, который содержит в себе Unicode коды символов в этой строке. В python нет отдельного типа данных под один символ, т.е. символ представляет собой строку длинны 1.
Строки — последовательности, а значит можно обратившись по индексу получить символ (строку из одного символа), но последовательности неизменяемые, т.е. изменить любой символ нельзя. Чтобы изменить строку, нужно создать новую.
Создать строки можно огромным количеством способом. Рассмотрим самые основные из них.
Можно задавать строки в одинарных (апострофы) и двойных кавычках:
s1 = 'Hello, world!' s2 = "Hello, world!"
Это может пригодиться, если вы хотите поместить внутрь строки символы кавычек первого или второго типа. Например, апостроф можно поместить внутри двойных кавычек, а двойные кавычки внутри одинарных:
s1 = '"Э́врика!" - Архимед' s2 = "What's up."
Можно задавать строки, допускающие перенос строки внутри, с помощью троекратных одинарных или двойных кавычек:
s = ''' "Modern programs must handle Unicode —
Python has excellent support for Unicode,
and will keep getting better" - Guido van Rossum.
'''
print(s)
"Modern programs must handle Unicode —
Python has excellent support for Unicode,
and will keep getting better" - Guido van Rossum.
Очень часто используется для документации функций.
def complex(real=0.0, imag=0.0):
"""Form a complex number.
Keyword arguments:
real -- the real part (default 0.0)
imag -- the imaginary part (default 0.0)
"""
if imag == 0.0 and real == 0.0:
return complex_zero
...
Также существует огромное количество функций и методов, которые позволяют создавать строки из уже существующих строк и объектов других видов.
Самый простой пример — функция str()
. Она преобразует объект в читабельную строку (если объект допускает это) и всегда неявно вызывается, если объект подаётся на вход функции print()
.
from math import pi
print(pi, str(pi))
3.141592653589793 3.141592653589793
Форматирование¶
Более гибкий подход для форматированного вывода — f-строки f-strings
.
an_int = 42
a_float = pi
name = "Иван"
s1 = f"{name} больше всего любит два числа: {an_int} и {a_float}"
print(s1)
Иван больше всего любит два числа: 42 и 3.141592653589793
print(f"{name} больше всего любит два числа: {an_int} и {a_float:.3f}")
years = [99, 100 , 101]
centuries = ["I", "I", "II"]
for i in range(3):
print(f"Год {years[i]} => {centuries[i]}-й век")
Иван больше всего любит два числа: 42 и 3.142
Год 99 => I-й век
Год 100 => I-й век
Год 101 => II-й век
for i in range(3):
print(f"Год {years[i]:3d} => {centuries[i]}-й век")
Год 99 => I-й век
Год 100 => I-й век
Год 101 => II-й век
Есть еще и минимум два подхода, которые позволяют заменить f-строки.
Форматирование в стиле
C
:
s = "%s больше всего любит два числа: %i и %f" % (name, an_int, a_float)
print(s)
print("%s больше всего любит два числа: %i и %.3f" % (name, an_int, a_float))
Иван больше всего любит два числа: 42 и 3.141593
Иван больше всего любит два числа: 42 и 3.142
Вызов метода
format()
у строки:
s = "{} больше всего любит два числа: {} и {}".format(name, an_int, a_float)
print(s)
print("{} больше всего любит два числа: {} и {:.3f}".format(name, an_int, a_float))
Иван больше всего любит два числа: 42 и 3.141592653589793
Иван больше всего любит два числа: 42 и 3.142
Какой из них выбрать — по большей части дело вкуса. Форматирование в стиле C
было введено в python вместе с самой первой версией языка и гарантированно будет работать на более старых версиях python. f-strings
введены сравнительно недавно и наверное самый наглядный способ форматирования строк.