Срезы
Contents
Срезы¶
Срезы встроенных коллекций python¶
Простые срезы¶
Списки, строки, кортежи и другие последовательности python
кроме обычной индексации поддерживают индексацию срезами (slices). Рассмотрим на примере строк.
import string
a = string.ascii_lowercase
a
'abcdefghijklmnopqrstuvwxyz'
Итак, у нас есть строка a
, состоящая из строчных символов английского алфавита.
Если мы хотим извлечь подстроку, начиная с позиции start
включая и заканчивая позицией stop
не включая, то применяется синтаксис
sequence[start:stop]
Например, если нас часть строки с 1-го по 5-ый символ (нумерация с нуля), мы запишем
print(a[1:5])
bcde
Если нас интересует подстрока с самого начала до какого-то индекс stop
, то допускается опускать первый параметр среза
print(a[0:3])
print(a[:3])
abc
abc
Аналогично, и со вторым параметром: если на интересует подстрока до самого конца исходный строки, то можно опустить второй параметр.
print(a[23:len(a)])
print(a[23:])
xyz
xyz
Если опустить оба параметра, то возвращается полная копия строки.
print(a[:])
abcdefghijklmnopqrstuvwxyz
Если в срезе выйти за пределы последовательности, то никакой ошибки не будет. Результатом будет та часть строки, что пересекается с выбранным срезом, которая может оказаться пустой, если индексы среза не пересекаются с допустимыми индексами строки.
# символы '|' по бокам, чтобы наглядно показать, что напечаталась пустая строка
print(f"|{a[-100:100]}|")
print(f"|{a[90:100]}|")
print(f"|{a[5:2]}|")
|abcdefghijklmnopqrstuvwxyz|
||
||
Срезы с шагом¶
Для разнообразия теперь будем брать срезы от списков.
b = list(range(10))
print(b)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Итак, у нас есть список b
чисел от 0 до 9.
Срезы с шагом позволяют извлекать элементы не подряд, а, например, с прореживанием. Для этого предыдущий синтаксис расширяется третьим параметром, который как раз и отвечает за шаг.
sequence[start:stop:step]
Например, чтобы вырезать каждый второй элемент списка b
, начиная с 1-го и заканчивая 8-м (не включая), необходимо записать.
print(b[1:8:2])
[1, 3, 5, 7]
Как и прежде, первые два параметра можно опускать, если start
и stop
совпадают с началом и концом списка соответственно.
Ниже берется каждый второй элемент всего списка.
print(b[::2])
[0, 2, 4, 6, 8]
Шаг может быть отрицательным, но тогда start
должен быть больше step
, чтобы вернулась не пустая подпоследовательность. При этом, как и до этого, start
попадает в срез, а stop
нет.
print(b[8:1:-1])
[8, 7, 6, 5, 4, 3, 2]
Получить последовательность в обратном порядке (обратить последовательность) можно синтаксисом
sequence[::-1]
print(a[::-1])
print(b[::-1])
zyxwvutsrqponmlkjihgfedcba
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
Присваивание по срезам¶
Если последовательность изменяемая (список, например), то допускается присваивание по срезу.
Если шаг не указан, то можно заменить элементы среза в списке элементами коллекции справа от оператора =
вне зависимости от того, совпадает ли их количество или нет
print(b)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Например, код в ячейке ниже заменяет первые два элемента списка b
на новые два элемента (0 заменяется на 3.14, 1 заменяется на 42).
b[:2] = [3.14, 42]
print(b)
[3.14, 42, 2, 3, 4, 5, 6, 7, 8, 9]
Код в ячейке ниже заменяет первые два элемента (3.14 и 42 после предыдущей замены) на сразу три новых элемента, тем самым расширяя список.
b[:2] = [0, 3.14, 42]
print(b)
[0, 3.14, 42, 2, 3, 4, 5, 6, 7, 8, 9]
Следующим синтаксисом можно удалить первых два элемента.
b[:2] = []
print(b)
[42, 2, 3, 4, 5, 6, 7, 8, 9]
Хотя для этого предпочтительнее воспользоваться синтаксисом
del b[:2]
Если шаг указан, то количество элементов в коллекции справа от =
должно совпадать с количеством элементов в срезе
b = list(range(10))
print(b[::2])
b[::2] = b[1::2]
print(b)
b[::2] = []
[0, 2, 4, 6, 8]
[1, 1, 3, 3, 5, 5, 7, 7, 9, 9]
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
Input In [16], in <cell line: 6>()
4 b[::2] = b[1::2]
5 print(b)
----> 6 b[::2] = []
ValueError: attempt to assign sequence of size 0 to extended slice of size 5
Срезы массивов NumPy
¶
Срезы NumPy
введут себя очень похоже с двумя основными отличиями (документация NumPy про срезы):
при присваивании по срезу размер массива не может измениться вне зависимости от того, указан шаг или нет;
они допускают многомерные срезы;
Если массив одномерный, то роль играет только первое отличие.
import numpy as np
x = np.arange(10)
print(x)
[0 1 2 3 4 5 6 7 8 9]
print(x[::2])
[0 2 4 6 8]
x[::2] = [1, 3, 5, 7, 9]
print(x)
[1 1 3 3 5 5 7 7 9 9]
Но если массив многомерный, то можно делать срезы сразу вдоль нескольких осей.
A = np.arange(100).reshape(10, 10)
print(A)
[[ 0 1 2 3 4 5 6 7 8 9]
[10 11 12 13 14 15 16 17 18 19]
[20 21 22 23 24 25 26 27 28 29]
[30 31 32 33 34 35 36 37 38 39]
[40 41 42 43 44 45 46 47 48 49]
[50 51 52 53 54 55 56 57 58 59]
[60 61 62 63 64 65 66 67 68 69]
[70 71 72 73 74 75 76 77 78 79]
[80 81 82 83 84 85 86 87 88 89]
[90 91 92 93 94 95 96 97 98 99]]
A[2:4, 2:4]
array([[22, 23],
[32, 33]])
A[:, ::2]
array([[ 0, 2, 4, 6, 8],
[10, 12, 14, 16, 18],
[20, 22, 24, 26, 28],
[30, 32, 34, 36, 38],
[40, 42, 44, 46, 48],
[50, 52, 54, 56, 58],
[60, 62, 64, 66, 68],
[70, 72, 74, 76, 78],
[80, 82, 84, 86, 88],
[90, 92, 94, 96, 98]])
Процесс получения двухмерного среза можно мысленно представить в следующем виде:
срез, указанный до запятой, вырезает строки из массива;
срез, указанный после запятой, вырезает столбцы из массива;
результирующий срез представляет из себя все то, что располагается на пересечении этих строк и столбцов.
Это позволяет получить i
-ю строку выражением
A[i, :]
а j
-й столбец выражением
A[:, j]
print(A[0, :])
[0 1 2 3 4 5 6 7 8 9]
print(A[:, 0])
[ 0 10 20 30 40 50 60 70 80 90]
Присваивание по срезу¶
Срезы ссылаются на данные исходного массива. По ним можно производить присваивание. Например, можно целиком заменить столбец матрицы.
A[:, 0] = np.arange(-10, 0)
print(A)
[[-10 1 2 3 4 5 6 7 8 9]
[ -9 11 12 13 14 15 16 17 18 19]
[ -8 21 22 23 24 25 26 27 28 29]
[ -7 31 32 33 34 35 36 37 38 39]
[ -6 41 42 43 44 45 46 47 48 49]
[ -5 51 52 53 54 55 56 57 58 59]
[ -4 61 62 63 64 65 66 67 68 69]
[ -3 71 72 73 74 75 76 77 78 79]
[ -2 81 82 83 84 85 86 87 88 89]
[ -1 91 92 93 94 95 96 97 98 99]]
В примере выше формы среза слева от “=
” и массива справа от “=
” совпадают. Это необязательное условие: работают правила броадкастинга. Например, прибавим к всем элемента первой строки 100, а каждый элемент последней строки сделаем равными 42.
A[0, :] += 100
A[-1, ::2] = 42
print(A)
[[ 90 101 102 103 104 105 106 107 108 109]
[ -9 11 12 13 14 15 16 17 18 19]
[ -8 21 22 23 24 25 26 27 28 29]
[ -7 31 32 33 34 35 36 37 38 39]
[ -6 41 42 43 44 45 46 47 48 49]
[ -5 51 52 53 54 55 56 57 58 59]
[ -4 61 62 63 64 65 66 67 68 69]
[ -3 71 72 73 74 75 76 77 78 79]
[ -2 81 82 83 84 85 86 87 88 89]
[ 42 91 42 93 42 95 42 97 42 99]]
Теперь возьмем все элементы на пересечении 4-6 строк и столбцов (включительно) и обнулим их.
A[3:7, 3:7] = 0
print(A)
[[ 90 101 102 103 104 105 106 107 108 109]
[ -9 11 12 13 14 15 16 17 18 19]
[ -8 21 22 23 24 25 26 27 28 29]
[ -7 31 32 0 0 0 0 37 38 39]
[ -6 41 42 0 0 0 0 47 48 49]
[ -5 51 52 0 0 0 0 57 58 59]
[ -4 61 62 0 0 0 0 67 68 69]
[ -3 71 72 73 74 75 76 77 78 79]
[ -2 81 82 83 84 85 86 87 88 89]
[ 42 91 42 93 42 95 42 97 42 99]]
Чуть более сложный пример.
end = f"\n{'-'*80}\n"
A[:, -3:] = np.array([1, 2, 3])
print(A, end=end)
A[::2, -3:] = np.array([4, 5, 6])
print(A, end=end)
A[-3:, :] = np.array([[7], [8], [9]])
print(A)
[[ 90 101 102 103 104 105 106 1 2 3]
[ -9 11 12 13 14 15 16 1 2 3]
[ -8 21 22 23 24 25 26 1 2 3]
[ -7 31 32 0 0 0 0 1 2 3]
[ -6 41 42 0 0 0 0 1 2 3]
[ -5 51 52 0 0 0 0 1 2 3]
[ -4 61 62 0 0 0 0 1 2 3]
[ -3 71 72 73 74 75 76 1 2 3]
[ -2 81 82 83 84 85 86 1 2 3]
[ 42 91 42 93 42 95 42 1 2 3]]
--------------------------------------------------------------------------------
[[ 90 101 102 103 104 105 106 4 5 6]
[ -9 11 12 13 14 15 16 1 2 3]
[ -8 21 22 23 24 25 26 4 5 6]
[ -7 31 32 0 0 0 0 1 2 3]
[ -6 41 42 0 0 0 0 4 5 6]
[ -5 51 52 0 0 0 0 1 2 3]
[ -4 61 62 0 0 0 0 4 5 6]
[ -3 71 72 73 74 75 76 1 2 3]
[ -2 81 82 83 84 85 86 4 5 6]
[ 42 91 42 93 42 95 42 1 2 3]]
--------------------------------------------------------------------------------
[[ 90 101 102 103 104 105 106 4 5 6]
[ -9 11 12 13 14 15 16 1 2 3]
[ -8 21 22 23 24 25 26 4 5 6]
[ -7 31 32 0 0 0 0 1 2 3]
[ -6 41 42 0 0 0 0 4 5 6]
[ -5 51 52 0 0 0 0 1 2 3]
[ -4 61 62 0 0 0 0 4 5 6]
[ 7 7 7 7 7 7 7 7 7 7]
[ 8 8 8 8 8 8 8 8 8 8]
[ 9 9 9 9 9 9 9 9 9 9]]