> For the complete documentation index, see [llms.txt](https://timosii.gitbook.io/py_tutorial/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://timosii.gitbook.io/py_tutorial/funkcii/funkcii-2.0.md).

# Функции. Продолжение

### Распаковка и упаковка аргументов

* Функции могут принимать неопределенное количество аргументов.
* Для того, чтобы функция приняла и обработала все переданные позиционные аргументы, используется синтаксис `*args:`

```python
def sum_all(*args):
    count = 0
    for n in args: # args это кортеж из переданных аргументов
        count += n
    return count
```

***Практика:*** напишите функцию, которая перемножает все переданные ей аргументы

* Это была упаковка аргументов. А так выглядит распаковка:

```python
def greet(name, age):
    print(f"Привет, {name}! Тебе {age} лет.")

person_info = ('Анна', 25) # это тип данных кортеж, содержит 2 элемента
greet(*person_info)  # Распаковываем кортеж и передаем его как два аргумента
```

* Вы увидели использование ***кортежей*** — это тип данных: неизменяемая последовательность. Поддерживает индексацию, срезы и большинство методов, используемых для других последовательностей.

### Параметры по умолчанию

* Параметры по умолчанию позволяют задать значения по умолчанию для аргументов функции. Если аргумент не передан при вызове функции, то будет использовано значение по умолчанию.

***Пример:***

```python
def greet(name, age=30):
    print(f"Привет, {name}! Тебе {age} лет.")

greet("Максим")  # Если не передан второй аргумент, age будет равно 30
```

В этом примере `age=30` устанавливает значение по умолчанию для аргумента `age`. Если `age` не передан при вызове, то будет использоваться 30.

***Практика:*** определите функцию *squaring* (возведение в квадрат), которая имеет два параметра — число *num* и степень *degree,* которая по умолчанию равна 2.

### Функции как объекты первого класса

* В Python функции являются объектами первого класса. Это означает, что их можно передавать в качестве аргументов другим функциям, возвращать как значения других функций и хранить в переменных или структурах данных как любой другой объект.&#x20;

```python
def hello(name):
    print(f"Hello, {name}!")
    
greeting_func = hello
```

```python
def apply(func, value):
    return func(value)

def double(x):
    return x * 2

result = apply(double, 5)
```

* Рассмотрим другой пример с передачей функции в качестве аргумента другой функции:

```python
def apply_function(numbers, function):
    results = []
    for number in numbers:
        result = function(number)
        results.append(result)
    return results
    
def square(number):
    return number ** 2
    
numbers = [1, 2, 3, 4, 5]
squared_numbers = apply_function(numbers, square)
```

***Практика:***

* Создайте функцию `filter_numbers(numbers, filter_func)`, которая принимает список чисел `numbers` и функцию `filter_func`. Функция `filter_numbers` должна вернуть новый список, содержащий только те числа из исходного списка, для которых `filter_func` возвращает `True`
* Пример использования:

```python
def is_even(x):
    return x % 2 == 0

numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
filtered_numbers = filter_numbers(numbers, is_even)
print(filtered_numbers)
# [2, 4, 6, 8, 10]
```

### Рекурсия

* Рекурсия — определение, описание, изображение какого-либо объекта или процесса внутри самого этого объекта или процесса, то есть ситуация, когда объект является частью самого себя.
* "Чтобы понять рекурсию, надо понять рекурсию"

<figure><img src="/files/R2dbEFkZFUjXEERqNNXR" alt=""><figcaption><p>Рекурсивное изображение экрана</p></figcaption></figure>

* Рекурсия в программировании — это вызов функции из неё самой
* Принцип рекурсии — при каждом вызове подзадача должна становиться проще задачи. Важно предусмотреть ***крайний случай*** — до которого мы будем углубляться

{% code overflow="wrap" %}

```python
def sum_range(n):
    if n == 1:
        return 1    
    return sum_range(n - 1) + n

# Что происходит? Функции при вызове внутри себя попадают в стек вызовов - по принципу стопки монет
# Как только функция достигнет крайнего случая - стек вызовов начнет выполнятся
# sum_range(5) - в стек
# sum_range(4) - в стек
# sum_range(3) - в стек
# sum_range(2) - в стек
# sum_range(1) - 1 - выполняется, т.к. мы предусмотрели крайний случай
# sum_range(2) - 3 - из стека и так далее
# sum_range(3) - 6
# sum_range(4) - 10
# sum_range(5) - 15
```

{% endcode %}

```python
def matryoshka(n):
    if n == 1:
        print("Матрёшечка")
    else:
        print("Верх матрёшки n=", n)
        matryoshka(n - 1)
        print("Низ матрёшки n=", n)
```

* Посмотрите на примеры функций. Попробуйте запустить код, проанализируйте результат

```python
def print_numbers(n):
    if n > 0:
        print_numbers(n - 1)
        print(n)
```

```python
def sum_list(lst):
    if not lst:
        return 0
    else:
        return lst[0] + sum_list(lst[1:])
```

***Практика:***&#x20;

* Напишите функцию, которая принимает число и возвращает его факториал
* Напишите рекурсивную функцию нахождения числа Фибоначчи. Функция принимает порядковый номер числа Фибоначчи и возвращает его значение. Дополнительно: реализуйте задачу при помощи *итеративной функции*
