Рекурсия

Порция синтаксиса Python

Функции

Функция - объект!

В исходном коде функция встречается несколько раз: во время объявления и во время вызова. При объявлении функции мы пользуемся блоком def. Когда интерпретатор языка прочтёт этот блок, то он создаст объект функции:

>>> def square(x):
...     return x**2
...
>>> square
<function square at 0x10a829290>

Этот объект <function square at HEX-ADDRESS>, как и любой другой, имеет имя-ссылку square и занимает место в памяти.

Вызов функции осуществляется путём дописывания пары круглых скобок вслед за именем функции. Внутри скобок через запятую перечисляют её аргументы.

>>> square(9)
81
>>> y = 71
>>> square(y)
5041

Коварство перегруженности

Давайте добавим ещё одну функцию:

>>> def mul(multiplier, element):
...     return multiplier * element
...
>>> mul(2, 8)
16
>>> mul(2, 10)
20

Она умножает первый аргумент на второй.

Перед вызовом функции её аргументы необходимо вычислить, Python это делает автоматически. Так, выражение (2*10)2 мы можем записать через наши функции:

>>> square(mul(2, 10))
400

Здесь для вызова square необходимо получить значение первого аргумента, для этого нужно прежде получить это значение от функции mul, и она вызывается первой.

Заметьте, что в функции mul используется оператор умножения *. Он является перегруженным, т.е. его поведение зависит от аргументов:

>>> 2 * 12
24
>>> '2' * 12
'222222222222'
>>> 2 * [0, 1, 2]
[0, 1, 2, 0, 1, 2]

По этой причине у функции mul ожидается аналогичное поведение:

>>> mul(2, 12)
24
>>> mul('2', 12)
'222222222222'
>>> mul(2, [0, 1, 2])
[0, 1, 2, 0, 1, 2]

Python не позволяет явно указать тип аргументов функции, например, невозможно разрешить вызовы mul (без проверки вручную) только от двух целых чисел. Однако, при объявлении функции мы можем пометить ожидаемые типы аргументов для тех, кто будет ею пользоваться:

>>> def mul(multiplier:int, element:int):
...     return multiplier * element
...
>>> mul(12, 12)
144
>>> mul(12, '12')
'121212121212121212121212'

Развёртывание итерируемых объектов

Рассмотрим функцию скалярного произведения на плоскости:

>>> def dot_product(x1, y1, x2, y2):
...     return x1*x2 + y1*y2

Допустим, в своём коде программист хранит координаты точки в структуре данных, например, в массиве размера 2:

>>> point_a = [1, 2]
>>> point_b = [3, 4]

и хочет получить их скалярное произведение. Два вызова ниже эквивалентны:

>>> dot_product(point_a[0], point_a[1], point_b[0], point_b[1])
11
>>> dot_product(*point_a, *point_b)
11

Во втором из них используется операция развёртывания, которая разворачивает значения массива и рассовывает их по аргументам функции.

Также сравните:

>>> print([1, 2, 'cs-mipt-ru', 4])
[1, 2, 'cs-mipt-ru', 4]
>>> print(*[1, 2, 'cs-mipt-ru', 4])
1 2 cs-mipt-ru 4

map

Функция map(func, iterable) применяет функцию func к итерируемому объекту iterable и возвращает map object - также итерируемый объект, на основе которого мы можем создать свой, если требуется.

Считывание массива чисел:

>>> s = input()
1 2 3 4 5
>>> s
'1 2 3 4 5'
>>> str_arr = s.split()
>>> str_arr
['1', '2', '3', '4', '5']
>>> int_arr = list( map(int, str_arr) )
>>> int_arr
[1, 2, 3, 4, 5]

Можно сделать тоже самое в одну строчку, поскольку функции можно вкладывать друг друга:

>>> int_arr = list( map( int, input().split() ) )
6 7 8 9 10
>>> int_arr
[6, 7, 8, 9, 10]

Последовательность такая: считать строку input, разбить её по пробелам и сформировать массив строк str.split(), применить функцию преобразования типа int к массиву строк map(int, list_of_str), сформировать список list(map object):

list( map(int, input().split()) )
list( map(int, '6 7 8 9 10'.split()) )
list( map(int, ['6', '7', '8', '9', '10']) )
list( map object  )
[6, 7, 8, 9, 10]

Функции максимального и минимального

Python предоставляет функции поиска максимального и минимального. Есть две возможности вызова

  • если аргумент один, то он считается итерируемым объектом и поиск осуществляется в нём (например, для списка)
  • если аргументов более одного, то поиск осуществляется между ними
>>> l = [-1, 2, 5, 10]
>>> max(l)
10
>>> min(l)
-1
>>> a = 10
>>> b = -2
>>> c = 0
>>> min(a, b, c)
-2
>>> max(a, b, c)
10

Срезы

Срез — это операция, формирующая новый массив на основе элементов существующего.

>>> l = list( range(10)  )
>>> l
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> l[5:]
[5, 6, 7, 8, 9]
>>> l[5:7]
[5, 6]
>>> l[:7]
[0, 1, 2, 3, 4, 5, 6]
>>> l[::2]
[0, 2, 4, 6, 8]
>>> l[2:9:3]
[2, 5, 8]

Работа последнего среза аналогична следующему коду:

>>> slice = []
>>> for i in range(2, 9, 3):
...     slice.append(l[i])
...
>>> slice
[2, 5, 8]
>>> l[2:9:3]
[2, 5, 8]