Типы последовательностей имеют общую концепцию первого элемента, второго элемента и так далее. В основном порядок элементов последовательности с использованием натуральных чисел. В Python (и многих других языках) начальный индекс равен 0
, а не 1
.
Таким образом, первый элемент имеет индекс 0
, второй элемент имеет индекс 1
и так далее.
Python имеет встроенные изменяемые и неизменяемые типы последовательностей.
Строки и кортежи неизменяемы — мы можем получить доступ, но не можем изменить содержимое последовательности:
In:
t = (1, 2, 3) t[0]
Вне:
1
In:
t[0] = 100 --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-3-155b9e8fb284> in <module>() ----> 1 t[0] = 100 TypeError: 'tuple' object does not support item assignment
Но, конечно, если последовательность содержит изменяемые объекты, то хотя мы и не можем модифицировать последовательность элементов (не можем заменять, удалять или вставлять элементы), мы точно можем изменять содержимое изменяемых объектов:
In:
t = ( [1, 2], 3, 4)
t
неизменяем, но его первый элемент является изменяемым объектом:
In:
t[0][0] = 100 t
Вне:
([100, 2], 3, 4)
Итерации
iterable — это просто то, что можно повторять, например, с помощью цикла for
:
In:
t = (10, 'a', 1+3j) s = {10, 'a', 1+3j} for c in t: print(c) Out: 10 a (1+3j)
In:
for c in s: print(c) Out: a 10 (1+3j)
Обратите внимание, как мы могли перебирать как кортеж, так и набор. Повторение кортежа сохранило порядок элементов в кортеже, но не для набора. В наборах нет порядка элементов — они итерабельны, но не последовательности.
Большинство типов последовательностей поддерживают операции in
и not in
. Диапазоны тоже работают, но не так эффективно, как списки, кортежи, строки и т. д.
In:
'a' in ['a', 'b', 100]
Вне:
True
In:
100 in range(200)
Вне:
True
Мин., макс. и длина
Последовательности также обычно поддерживают метод len
для получения количества элементов в коллекции. Некоторые итерации также могут поддерживать этот метод.
In:
len('python'), len([1, 2, 3]), len({10, 20, 30}), len({'a': 1, 'b': 2})
Вне:
(6, 3, 3, 2)
Последовательности (и даже некоторые итерации) могут поддерживать max
и min
, если типы данных в коллекции могут быть в некотором смысле упорядочены (<
или >
).
In:
a = [100, 300, 200] min(a), max(a)
Вне:
(100, 300)
In:
s = 'python' min(s), max(s)
Вне:
('h', 'y')
In:
s = {'p', 'y', 't', 'h', 'o', 'n'} min(s), max(s)
Вне:
('h', 'y')
Но если элементы не имеют определенного порядка:
In:
a = [1+1j, 2+2j, 3+3j] min(a) --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-17-b0cd79e53377> in <module>() 1 a = [1+1j, 2+2j, 3+3j] ----> 2 min(a) TypeError: '<' not supported between instances of 'complex' and 'complex'
min
и max
будут работать для разнородных типов, если элементы попарно сравнимы (определены <
или >
).
Например:
In:
from decimal import Decimal t = 10, 20.5, Decimal('30.5') min(t), max(t)
Вне:
(10, Decimal('30.5'))
In:
t = ['a', 10, 1000] min(t) --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-21-983eac063887> in <module>() 1 t = ['a', 10, 1000] ----> 2 min(t) TypeError: '<' not supported between instances of 'int' and 'str'
Даже объекты range
поддерживают min
и max
:
In:
r = range(10, 200) min(r), max(r)
Вне:
(10, 199)
Конкатенация
Мы можем объединять последовательности с помощью оператора +
:
In:
[1, 2, 3] + [4, 5, 6]
Вне:
[1, 2, 3, 4, 5, 6]
In:
(1, 2, 3) + (4, 5, 6)
Вне:
(1, 2, 3, 4, 5, 6)
Обратите внимание, что тип объединенного результата совпадает с типом объединяемых последовательностей, поэтому объединение последовательностей разных типов не будет работать:
In:
(1, 2, 3) + [4, 5, 6] --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-25-67a9e2ed8695> in <module>() ----> 1 (1, 2, 3) + [4, 5, 6] TypeError: can only concatenate tuple (not "list") to tuple
In:
'abc' + ['d', 'e', 'f'] --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-26-8cbdd441adc1> in <module>() ----> 1 'abc' + ['d', 'e', 'f'] TypeError: must be str, not list
Примечание: если вы действительно хотите объединить различные типы, вам придется сначала преобразовать их в общий тип:
In:
(1, 2, 3) + tuple([4, 5, 6])
Вне:
(1, 2, 3, 4, 5, 6)
In:
tuple('abc') + ('d', 'e', 'f')
Вне:
('a', 'b', 'c', 'd', 'e', 'f')
In:
''.join(tuple('abc') + ('d', 'e', 'f'))
Вне:
'abcdef'
Повторение
Большинство типов последовательностей также поддерживают повторение, то есть объединение одной и той же последовательности целое число раз:
In:
'abc' * 5
Вне:
'abcabcabcabcabc'
In:
[1, 2, 3] * 5
Вне:
[1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3]
Чуть позже мы вернемся к некоторым предостережениям относительно конкатенации и повторения.
Поиск вещей в последовательностях
Мы можем найти индекс вхождения элемента в последовательность:
In:
s = "gnu's not unix" s.index('n')
Вне:
1
In:
s.index('n', 1), s.index('n', 2), s.index('n', 8)
Вне:
(1, 6, 11)
Исключение возникает, если элемент не найден, поэтому вам нужно перехватить его, если вы не хотите, чтобы ваше приложение вылетало:
In:
s.index('n', 13) --------------------------------------------------------------------------- ValueError Traceback (most recent call last) <ipython-input-35-d038ca109973> in <module>() ----> 1 s.index('n', 13) ValueError: substring not found
In:
try: idx = s.index('n', 13) except ValueError: print('not found') Out: not found
Обратите внимание, что эти методы поиска объектов в последовательностях не предполагают, что объекты в последовательности каким-либо образом упорядочены. В основном они выполняют поиск, который перебирает последовательность, пока не найдет (или не найдет) запрошенный элемент.
Если у вас есть отсортированная последовательность, то доступны другие методы поиска, например бинарный поиск. Я раскрою некоторые из этих тем в дополнительном разделе этого курса.
Нарезка
Мы вернемся к нарезке в одной из следующих лекций, но типы последовательностей обычно поддерживают нарезку, даже диапазоны (начиная с Python 3.2). Как и при конкатенации, слайсы возвращают тот же тип, что и срезаемая последовательность:
In:
s = 'python' l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]] s[0:3], s[4:6]
Вне:
('pyt', 'on')
In:
l[0:3], l[4:6]
Вне:
([1, 2, 3], [5, 6])
Можно расширять диапазоны за пределы последовательности:
In:
s[4:1000]
Вне:
'on'
Если ваш первый аргумент в срезе — 0
, вы можете его даже опустить. Отсутствие второго аргумента означает, что он будет включать все остальные элементы:
In:
s[0:3], s[:3]
Вне:
('pyt', 'pyt')
In:
s[3:1000], s[3:], s[:]
Вне:
('hon', 'hon', 'python')
У нас даже может быть расширенная нарезка, которая обеспечивает начало, остановку и шаг:
In:
s, s[0:5], s[0:5:2]
Вне:
('python', 'pytho', 'pto')
In:
s, s[::2]
Вне:
('python', 'pto')
Технически мы также можем использовать отрицательные значения в срезах, включая расширенные срезы (подробнее об этом позже):
In:
s, s[-3:-1], s[::-1]
Вне:
('python', 'ho', 'nohtyp')
In:
r = range(11) # numbers from 0 to 10 (inclusive) print(r) print(list(r)) Out: range(0, 11) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
In:
print(r[:5]) Out: range(0, 5)
In:
print(list(r[:5])) [0, 1, 2, 3, 4]
Как видите, разрезание диапазона также возвращает объект диапазона, как и ожидалось.
Хеширование
Неизменяемые последовательности обычно поддерживают метод hash
, который мы подробно обсудим в разделе, посвященном типам отображения:
In:
l = (1, 2, 3) hash(l)
Вне:
2528502973977326415
In:
s = '123' hash(s)
Вне:
-1892188276802162953
In:
r = range(10) hash(r)
Вне:
-6299899980521991026
Но изменяемые последовательности (и изменяемые типы в целом) этого не делают:
In:
l = [1, 2, 3] hash(l) --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-54-22dd9c98a095> in <module>() ----> 1 hash(l) TypeError: unhashable type: 'list'
Также обратите внимание, что хешируемая последовательность больше не является хэшируемой, если один (или несколько) ее элементов не хешируются:
In:
t = (1, 2, [10, 20]) hash(t) --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-55-30cac1c4a226> in <module>() 1 t = (1, 2, [10, 20]) ----> 2 hash(t) TypeError: unhashable type: 'list'
Но это сработает:
In:
t = ('python', (1, 2, 3)) hash(t)
Вне:
-8790163410081325536
Как правило, неизменяемые типы, скорее всего, хэшируются, а неизменяемые — нет. Таким образом, числа, строки, кортежи и т. д. можно хэшировать, а списки и наборы — нет:
In:
from decimal import Decimal d = Decimal(10.5) hash(d)
Вне:
1152921504606846986
Наборы не хэшируются:
In:
s = {1, 2, 3} hash(s) --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-58-2216931a6bc4> in <module>() 1 s = {1, 2, 3} ----> 2 hash(s) TypeError: unhashable type: 'set'
Но замороженные наборы, неизменный вариант набора, это:
In:
s = frozenset({1, 2, 3}) hash(s)
Вне:
-7699079583225461316
Предостережения относительно конкатенации и повторения
Учти это:
In:
x = [2000] id(x[0])
Вне:
2177520743920
In:
l = x + x l
Вне:
[2000, 2000]
In:
id(l[0]), id(l[1])
Вне:
(2177520743920, 2177520743920)
Как и ожидалось, объекты в l[0]
и l[1]
одинаковы.
Можно также использовать:
In:
l[0] is l[1]
Вне:
True
Это не имеет большого значения, если объединяемые объекты неизменяемы. Но если они изменчивы:
In:
x = [ [0, 0] ] l = x + x l
Вне:
[[0, 0], [0, 0]]
In:
l[0] is l[1]
Вне:
True
И тогда мы имеем следующее:
In:
l[0][0] = 100 l[0]
Вне:
[100, 0]
In:
l
Вне:
[[100, 0], [100, 0]]
Обратите внимание, как изменение 1-го элемента 1-го элемента также изменило 1-й элемент второго элемента.
Хотя это кажется довольно очевидным при конкатенации с использованием оператора +
, как мы только что сделали, то же самое на самом деле происходит с повторением и может показаться не столь очевидным:
In:
x = [ [0, 0] ] m = x * 3 m
Вне:
[[0, 0], [0, 0], [0, 0]]
In:
m[0][0] = 100 m
Вне:
[[100, 0], [100, 0], [100, 0]]
А ведь даже x
изменился:
In:
x
Вне:
[[100, 0]]
Если вы действительно хотите, чтобы эти повторяющиеся объекты были разными объектами, вам придется как-то их копировать. Здесь хорошо сработает простое понимание списка:
In:
x = [ [0, 0] ] m = [e.copy() for e in x*3] m
Вне:
[[0, 0], [0, 0], [0, 0]]
In:
m[0][0] = 100 m
Вне:
[[100, 0], [0, 0], [0, 0]]
In:
x
Вне:
[[0, 0]]