## ++Тема 2++<br>Расширенные возможности функций
(c) Яценко Р.Н., 2019-2022
[Учебный центр компьютерных технологий "Кит"](http://kit.kh.ua/)
<img src="https://i.imgur.com/Kh901c1.png" style="width: 150px; position: fixed; top: 10px; right: 10px; border: 0; box-shadow: none;">
---
## 1. Виды параметров
----
### Структуризация программы
- декомпозиция сложной задачи на несколько более простых подзадач: это один из двух главных инструментов структурного программирование (второй -- структуры данных)
- уменьшение дублирования кода и возможность повторного использования кода в нескольких программах -- следование принципу DRY «не повторяйся» (англ. Don’t Repeat Yourself)
----
- распределение большой задачи между несколькими разработчиками или стадиями проекта
- сокрытие деталей реализации от пользователей подпрограммы
- улучшение отслеживания выполнения кода (большинство языков программирования предоставляет стек вызовов подпрограмм)
----
### Функции в Python
```python
def function_name([parameters]):
# Тело функции
suite
```
- змеиный_регистр (англ. snake_case) для наименования функций: `my_function`
- `parameters`: параметры функции (через запятую)
- пустые строки для отделения функций, а большие блоки кода помещать внутрь функций
- строки документации
----
### Пустая функция (stub)
- Иногда, когда вы пишете какой-нибудь код, вам нужно просто ввести определения функции, которое не содержит в себе код
```python
def empty_function():
pass
```
- Оператор `pass` ничего не делает
----
### Передача аргументов функции
```python=
def add(a, b):
return a + b
print( add(1, 2) ) # 3
```
Каждая функция выдает определенный результат. Если вы не указываете на выдачу конкретного результата, она выдаст результат `None`
----
### Передача параметров и возвращение результата функции

----
### Виды параметров
- *позиционные*: указываются простым перечислением:
```python
def function_name(a, b, c):
# a, b, c - 3 позиционных параметра
pass
```
- *ключевые*: указываются перечислением `ключ=значение`:
```python
def function_name(key=value, key2=value2):
# key, key2 - 2 ключевых параметра
# value, value2 - их значения по умолчанию
```
----
### Наименование аргументов
```python=6
print( add(a = 2, b = 3) ) # 5
total = add(b = 4, a = 5)
print(total) # 9
```
Не важно, в каком порядке вы будете передавать аргументы функции до тех пор, пока они называются корректно
----
### Ключевые аргументы
```python=
def keyword_function(a=1, b=2):
return a+b
print(keyword_function(b=4, a=5)) # 9
print(keyword_function()) # Результат - 3
# а и b по умолчанию имеют значение 1 и 2
```
----
### Комбинация параметров
**Позиционные параметры всегда идут перед ключевыми**:
```python=
def mixed_function(a, b=2, c=3):
return a+b+c
print( mixed_function(1, b=4, c=5) ) # 10
print( mixed_function(1) ) # 6
print(mixed_function(b=4, c=5))
```
```
Traceback (most recent call last):
File "<string>", line 1, in <fragment>
TypeError: mixed_function() takes at least 1 argument (2 given)
```
---
## 2. Переменное количество аргументов
----
### Упаковка аргументов
Для переменного количества аргументов используется механизм упаковки аргументов, указав при объявлении параметра в функции один из двух символов:
- `*`: все позиционные аргументы начиная с этой позиции и до конца будут собраны в кортеж
- `**`: все ключевые аргументы начиная с этой позиции и до конца будут собраны в словарь
----
### Проблема
```python=
def multiply(x, y):
print (x * y)
print(multiply(5, 4)) # 20
print(multiply(5, 4, 3))
```
```
TypeError: multiply() takes 2 positional arguments but 3 were given
```
----
### Решение
Итак, если вам позже нужно будет использовать больше аргументов, вы можете использовать `*args` в качестве параметра
```python=
def multiply(*args):
z = 1
for num in args:
z *= num
print(z)
multiply(4, 5) # 20
multiply(10, 9) # 90
multiply(2, 3, 4) # 24
multiply(3, 5, 10, 6) # 900
```
----
### kwargs
- `**kwargs` с двумя звездочками используется в качестве параметра для отправки в функции списка аргументов переменной длины без ключевого слова
- в функции создается словарь под названием `kwargs`, и мы можем работать с ним так же, как и с другими словарями
----
### Пример
```python=
def print_kwargs(**kwargs):
print(kwargs)
print_kwargs(kwargs_1="Shark", kwargs_2=4.5, kwargs_3=True)
```
```
{'kwargs_1': 'Shark', 'kwargs_2': 4.5, 'kwargs_3': True}
```
----
### Пример 2
```python=
def print_values(**kwargs):
for key, value in kwargs.items():
print(f"Значением для {key} является {value}")
print_values(my_name="Глеб", your_name="Сергей")
```
```
Значение для my_name является Глеб
Значение для your_name является Сергей
```
----
### Порядок параметров функции
- позиционные параметры
- `*args`
- ключевые параметры
- `**kwargs`
```python
def example2(arg_1,
arg_2,
*args,
kw_1="shark",
kw_2="blobfish",
**kwargs):
```
----
### Использование `*args` при вызове функции
```python=
def some_args(arg_1, arg_2, arg_3):
print("arg_1:", arg_1)
print("arg_2:", arg_2)
print("arg_3:", arg_3)
args = ("Глеб", "Сергей", "Кирилл")
some_args(*args)
```
```
arg_1: Глеб
arg_2: Сергей
arg_3: Кирилл
```
----
```python=
def some_args(arg_1, arg_2, arg_3):
print("arg_1:", arg_1)
print("arg_2:", arg_2)
print("arg_3:", arg_3)
my_list = [2, 3]
some_args(1, *my_list)
```
```
arg_1: 1
arg_2: 2
arg_3: 3
```
----
### Использование `*kwargs` при вызове функции
```python=
def some_kwargs(kwarg_1, kwarg_2, kwarg_3):
print("kwarg_1:", kwarg_1)
print("kwarg_2:", kwarg_2)
print("kwarg_3:", kwarg_3)
kwargs = {
"kwarg_1": "Глеб",
"kwarg_2": "Сергей",
"kwarg_3": "Кирилл"
}
some_kwargs(**kwargs)
```
```
kwarg_1: Глеб
kwarg_2: Сергей
kwarg_3: Кирилл
```
---
## 3. Анонимные функции
----
### Синтаксис лямбда-выражения
```python
lambda parameters: expression
```
- `parameters` является необязательной, представляет собой список переменных через запятую (позиционных параметров)
- `expression` не может содержать `if, for, while, return` (условные выражения - допустимы)
- результатом лямбда-выражения является анонимная функция
- Когда лямбда-функция вызывается, она возвращает выражение `expression`
----
### Пример
```python=
import math
def sqroot(x):
return math.sqrt(x)
square_rt = lambda x: math.sqrt(x)
print(sqroot(49)) # 7.0
print(square_rt(64)) # 8.0
```
----
### Пример 1
Можно написать лямбда-функцию, которая удваивает свой аргумент: `lambda x: x*2`, и использовать её в функции `map`, чтобы удвоить все элементы в списке
```python=
my_list = [1, 2, 3, 4, 5, 6]
new_list = list(map(lambda x: x*2, my_list))
print(new_list) # [2, 4, 6, 8, 10, 12]
```
----
### Пример 2
Можно создать лямбда-функцию, которая ищет числа больше нуля: `lambda x: x > 0` и использовать в `filter`, чтобы создать список исключительно положительных чисел
```python=
my_list = [18, -3, 5, 0, -1, 12]
new_list = list(filter(lambda x: x > 0, my_list))
print(new_list) # [18, 5, 12]
```
----
### Пример 3
Пользовательская сортировка
```python=
elements = [(2, 12, "Mg"), (1, 11, "Na"),
(1, 3, "Li"), (2, 4, "Be")]
elements.sort(key=lambda x: x[1])
# Сортировка по порядковому номеру
print(elements)
# [(1, 3, 'Li'), (2, 4, 'Be'), (1, 11, 'Na'), (2, 12, 'Mg')]
lst = ["Java", "Algol", "C++"]
print(max(lst, key=lambda x: x.count("a")))
def find_a(x):
return x.find("a")
print(min(lst, key=find_a))
```
---
## 4. Рекурсия и мемоизация
----
### Последовательность Фибоначчи
Как вы, вероятно, знаете, последовательность Фибоначчи образуется следующим образом:
- складываем первое и второе число, 0 и 1, чтобы получить третье число в последовательности (0 + 1 = 1)
- складываем второе и третье число, чтобы получить 4-е число в последовательности
(1 + 1 = 2)
- и так проделываем для каждого последующего числа Фибоначчи
----
### Цикл for
```python=
def fib_for(n):
n1 = 0
n2 = 1
if n <= 0:
return 'Введите число больше 0'
elif n == 1:
return n1
elif n == 2:
return n2
else:
for i in range(2, n):
n1, n2 = n2, n1 + n2
return n2
```
----
### Рекурсия
fib(2) = fib(1) + fib(0) = 1 + 0 = 1

----
### Рекурсия (код)
```python=
def fib_recursion(n):
if n <= 0:
return 'Введите число больше 0'
elif n == 1:
return 0
elif n == 2:
return 1
else:
return fib_recursion(n - 1) + fib_recursion(n - 2)
```
----
### Мемоизация
*Мемоизация* – это метод, который может значительно улучшить производительность рекурсивной функции за счет уменьшения вычислительных затрат. Он сохраняет результаты дорогостоящих вызовов функций в массиве или словаре и при вызове с такими же входными данными возвращает кэшированные результаты.
----
### Мемоизация (код)
```python=
def Fib_memoisation(n, memo):
if n <= 0:
return 'Введите число больше 0'
elif n == 1:
return 0
elif n == 2:
return 1
else:
if n not in memo:
memo[n] = Fib_memoisation(n - 1, memo) \
+ Fib_memoisation(n - 2, memo)
return memo[n]
```
---
## 5. Документирование кода
----
### Строки документации
- Python предоставляет возможность добавить документацию (описание) к любой функции, используя строки документирования
- Это обычные строки в тройных кавычках `"""`, которые следуют сразу за строкой с инструкцией def перед программным кодом функции и воспринимаются транслятором специальным образом
- Соглашения по документированию функций содержатся в документе [PEP 257](https://www.python.org/dev/peps/pep-0257/)
----
### Документирование функций
```python=
# Для небольшой функции можно использовать одну строку
# Как правило, это описание действия в повелительном наклонении
def sqrt(x):
"""Вернуть квадратный корень из 'x'."""
return x**0.5
```
----
### Многострочное документирование
```python=
def heron(a, b, c):
"""Вернуть площадь треугольника по формуле Герона.
Параметры:
- a, b, c (float): стороны треугольника.
Результат:
- float: значение площади.
- None: если треугольник не существует.
"""
if not (a + b > c and a + c > b and b + c > a):
return
p = (a + b + c) / 2
return sqrt(p * (p - a) * (p - b) * (p - c))
```
----
### Получение документации
```python=
# Справочную информацию о функции можно получить 2 способами:
# heron.__doc__ или help(heron)
print(heron.__doc__)
```
```
Вернуть площадь треугольника по формуле Герона.
Параметры:
- a, b, c (float): стороны треугольника.
Результат:
- float: значение площади.
- None: если треугольник не существует.
```
---
## Спасибо за внимание!

(c) Яценко Р.Н., 2018-2022
[Учебный центр компьютерных технологий "Кит"](http://kit.kh.ua/)
<!--
- Источники
- https://python-scripts.com/functions-python
- https://python-scripts.com/def-args-kwargs
- https://python-scripts.com/lambda
- https://www.yuripetrov.ru/edu/python/ch_05_01.html
- [Лямбда-функции в Python: примеры](https://pythonist.ru/lyambda-funkczii-v-python-primery)
- [Мемоизация, рекурсия и цикл for в Python](https://pythonist.ru/memoizacziya-rekursiya-i-czikl-for-v-python/)
> Рассмотрим мемоизацию, рекурсию и цикл for для создания чисел Фибоначчи -->
{"metaMigratedAt":"2023-06-15T16:39:13.871Z","metaMigratedFrom":"YAML","title":"Тема 2. Расширенные возможности функций","breaks":false,"slideOptions":"{\"allottedMinutes\":80,\"transition\":\"slide\",\"theme\":\"beige\",\"slideNumber\":\"c\",\"spotlight\":{\"enabled\":true}}","description":"© Яценко Р.Н., 2019-2022","contributors":"[{\"id\":\"93a8c43f-1b5b-4461-9101-89b183ccbc1c\",\"add\":12742,\"del\":773}]"}