суббота, 2 января 2021 г.

ЛМНты Python, 96 - 100

>>> Декартово произведение

Декартово произведение последовательностей или множеств можно получить с помощью спискового включения или множественного включения. Например:

>>> [(x, y) for x in (1, 2) for y in (-2, -1)]
[(1, -2), (1, -1), (2, -2), (2, -1)]
>>>
>>> {(x, y) for x in {1, 2} for y in {-2, -1}}
{(1, -1), (2, -2), (1, -2), (2, -1)}

В модуле itertools имеется функция product для получения декартовых произведений. Получим тот же результат с ее помощью.

>>> from itertools import product
>>> [(i, j) for i, j in product((1, 2), (-2, -1))]
[(1, -2), (1, -1), (2, -2), (2, -1)]
>>>
>>> {(i, j) for i, j in product({1, 2}, {-2, -1})}
{(1, -1), (2, -2), (1, -2), (2, -1)}

С помощью product также можно умножить последовательность или множество на себя один или больше раз:

>>> [(i, j) for i, j in product((1, 2), repeat=2)]
[(1, 1), (1, 2), (2, 1), (2, 2)]
>>> {(i, j, k) for i, j, k in product({1, 2}, repeat=3)}
{(1, 2, 1), (2, 1, 1), (2, 2, 2), (1, 1, 2), (1, 2, 2), (2, 1, 2), (2, 2, 1), (1, 1, 1)}

>>> Срез

Срез работает для объектов list, tuple, str, bytes и для всех объектов, классы которых реализуют методы __getitem__ и __setitem__. Несколько примеров:

>>> [0, 1, 2, 3, 4][1:4]
[1, 2, 3]
>>> (0, 1, 2, 3, 4)[1:4]
(1, 2, 3)
>>> 'abcde'[1:4]
'bcd'
>>> b'abcde'[1:4]
b'bcd'
>>> [x**2 for x in range(10)][1:4]
[1, 4, 9]

Менее очевидный пример (см. также Почти как последовательность):

>>> range(10)[1:4]
range(1, 4)
Это работает, поскольку класс range реализует метод __getitem__.
>>> >>> range.__getitem__
<slot wrapper '__getitem__' of 'range' objects>

>>> __getitem__ и __setitem__

Реализация классом dunder-методов __getitem__ и __setitem__ позволяет работать с объектами этого класса с помощью оператора []. Например:

>>> class Example():
...     def __getitem__(self, s):
...         return s
...
...     def __setitem__(self, s, val):
...         print(s, val)
...
>>> e = Example()
>>> e[0] = 3.14
0 3.14
>>> e[1]
1

В качестве параметра s методы __getitem__ и __setitem__ могут получить не только числовой индекс, но и срез - объект класса slice:

>>> e[0:3] = (1, 2, 3, 4, 5)
slice(0, 3, None) (1, 2, 3, 4, 5)
>>> e[0:3]
slice(0, 3, None)

И должны реализовавать логику работы со срезом.

>>> Срез может

Срез производит новую последовательность, кроме случаев, когда он используется слева от знака присваивания. Срез, для которого не указаны нижняя и верхняя границы, создает копию последовательноти:

>>> x = [0, 1, 2, 3, 4, 5]
>>> y = x[:]
>>> y
[0, 1, 2, 3, 4, 5]
>>> x is not y
True

Разумеется, копия создается только для изменяемой последовательности. Для неизменяемой - не создается:

>>> x = (0, 1, 2, 3, 4, 5)
>>> y = x[:]
>>> y
(0, 1, 2, 3, 4, 5)
>>> x is y
True

Присваивание срезу без нижней и верхней границ замещает все элементы существующей последовательности, а не создает новую, что видно по неизменному id списка x:

>>> id(x)
2256739361480
>>> x[:] = [7, 7, 7]
>>> x
[7, 7, 7]
>>> id(x)
2256739361480

>>> Срез умеет

Кроме нижней и верхней границы, срез имеет третий параметр - шаг, по умолчанию равный 1. Значение шага N > 1 позволяет получить каждый N-ый элемент среза, заданного нижней и верхней границами, причем элементы рассматриваются слева направо:

>>> x = [0, 1, 2, 3, 4]
>>> x[::2]  # каждый 2-й элемент
[0, 2, 4]

Отрицательные значения шага предписывают рассматривать элементы среза справа налево:

>>> x[::-1]  # список в обратном порядке
[4, 3, 2, 1, 0]
>>> x[::-2]  # каждый 2-й элемент в обратном порядке
[4, 2, 0]
Срез с шагом -1 позволяет реверсировать строку:
>>> 'Привет, Python!'[::-1]
'!nohtyP ,тевирП'

Срез позволяет задавать границы за пределами последовательности - и это не приводит к ошибке:

>>> [0, 1, 2][:5]  # первые 5 элементов
[0, 1, 2]
>>> [0, 1, 2][-5:] # последние 5 элементов
[0, 1, 2]
>>> [0, 1, 2][::5] # каждый 5-й элемент
[0]
>>> [0, 1, 2][5:]  # элементы начиная с индекса 5
[]
>>> [0, 1, 2][:-5] # элементы по индекс 5
[]

Комментариев нет:

Отправить комментарий