# Basics_06. Функции

## Терминология на урок:

**Функцией** называется отдельный блок программы. У него может быть имя, он может принимать аргументы, с которыми потом будет работать, и может возвращать результат своей работы.

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

**Аргументами** или **фактическими параметрами** называются переменные (выражения, литералыи пр.), переданные в функцию при вызове. По сути, аргументы - это источник реальных значений параметров функции при ее вызове.

**Литерал** - фактическое значение, которое записывается в переменную или передается в функцию. К примеру, в записи `x = 1` литералом является 1.

## Функции

Можно рассмотреть функцию как черный ящик, в который вкладывают параметры. Ящик что-то делает, вы можете даже не знать, что именно, а потом предоставляет вам возможность работать с результатами своей работы.

![](https://jointhejoy.ru/sites/default/files/imagecache/post_image/%D0%91%D0%B5%D0%B7%D1%8B%D0%BC%D1%8F%D0%BD%D0%BD%D1%8B%D0%B9_2.png)


### Создание, объявление функции (function declaration )

В Python функция определяется или декларируется так:

```python
def say_hello():
    return "Hello"
    
res = say_hello()
```

Важнейшие элементы функции:

- Ключевое слово **`def`** говорит о том, что дальше будет функция.
- После **`def`** следует название функции, приведенная выше называется `say_hello`.
- Затем следуют скобки с параметрами функции. 
- После закрывающей скобки идет двоеточие. Как мы знаем, двоеточие в Python обозначает, что далее будет некий блок кода, выделенный отступами. В случае с условием это блок условия, тело условия, в случае с циклом - тело цикла, в случае с функцией - **тело функции или код функции**.
- Ключевое слово **`return`** используется в теле функции для того, чтобы завершить работу функции и вернуть ее результат. Если не написать return, функция вернет `None`. Хорошим тоном считается возвращать какое-нибудь значение явно.
- Результат работы функции - результат выполнения функции, который она возвращает. В приведенном примере результатом работы функции будет строка `"Hello"`, из функции она вернется благодаря ключевому слову `return` и поместится в переменную `res`.


Схематически, функцию можно изобразить следующим образом:

```python
def имя_функции():
    # Тело функции
    # Внутри тела функции прозводятся разные вычисления

    """
    Внутри тела функции могут производиться любые действия.
    К примеру, условие:
    """ 
    a = 10
    
    if a < 20:
        return "Меньше" # возвращаемое значение
    elif a > 20:
        return "Больше" # и это возвращаемое значение
    else:
        return "Что то пошло не так..." 
        # и это возвращаемое значение
    
```

В Python принято тело функции, как и тело любого блока, выделять отступом в 4 пробела.

**Все, что мы обсуждаем - только лишь создание функции, но не использование ее. Для использования функции ее необходимо вызвать.**

### Вызов функции (call function)

Функции создают по несольким причинам:

1. Дать имя кусочку кода.
2. Иметь возможность вызывать один и тот же кусочек кода несколько раз.
3. Абстрагироваться от реализации какого-то решения задачи, которое уже есть.
4. Объединить несколько действий в условное "одно" действие.

В любом случае созданную (объявленную) функцию необходимо вызвать, чтобы она выполнила свою задачу. До тех пор, пока функция не вызвана, она бесполезна и ничего не делает.

Вызовем нашу функцию **`say_hello:`**

```python
say_hello() # выведет Hello

```

Синтаксис вызова функции:

```python
имя_функции() # скобки обязательны при вызове функции
```

Если просто написать функцию и вызвать ее, мы не увидим возвращаемый результат. Он вернется, но после этого потеряется. Для того, чтобы этот результат не потерять, его нужно передать в работу другой функции, сохранить в переменную или любым другим образом поймать обработать. К примеру, поместим ее в функцию print в качестве аргумента:

```python
print(say_hello()) # Hello
```

### Параметры и аргументы функции

Как правило, функция имеет некоторые параметры, с которыми она работает. 

Пример из математики, где `y` - функция от `x`:

```
y(x) = 2x
y(x) = −5x + 2
y(x) = 12x2−1
```

В Python функции так же могут принимать параметры:

```python
def plus(num1, num2):
    result = num1 + num2
    return result
```

Здесь **num1** и **num2** - параметры функции **`plus`**.

#### Аргументы или парметры?

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

В Python, если вы декларируете параметры, то вы обязаны их туда передать, иначе получите ошибку. Вызовем нашу функцию **`plus`** с двумя числами-аргументами - **10** и **5**:

```python
ten = 10
five = 5

plus(ten, five)
# функция вывела 15

plus(10, 5)
# функция вывела 15

```

В момент выполнения функции параметры `num1` и `num2` примут значения `10` и `5` соостветственно.

Так же в python можно передавать аргументы не по порядку, но с явным указанием аргумента и соответствию аргумента передаваемому значению

```python
plus(num1=10, num2=5)

plus(num2=5, num1=10)
```

Как видно из примера выше, аргументы можно передавать и в переменных, и литералами.

### Виды параметров функции


```python
def func(positional, keyword=value, *args, **kwargs)
	pass

```

В Python можно различить 4 вида параметров у функции:


1. **Позиционные аргументы** обязательны и не имеют значений по умолчанию.
2. **Аргументы с ключевым словом** необязательны и имеют значения по умолчанию.
3. **Список с произвольным количеством аргументов** необязателен и не имеет значений по умолчанию.
4. **Словарь с произвольным количеством аргументов с ключевым словом** необязателен и не имеет значений по умолчанию.

### Функция как тип данных

Все в Python является объектом. Функции не выделяются и тоже являются объектами. Как и любые други типы данных, функции можно хранить в переменных, передавать параметрами в другие функции и хранить в словарях, списках и кортежах:

Пример передачи функции в качестве параметра:

```python
def run_operation(num1, num2, operation):
    return operation(num1, num2)

def plus(num1, num2):
    return num1 + num2

def minus(num1, num2):
    return num1 - num2

run_operation(10, 5, plus)

# 15

run_operation(10, 5, minus)

# 5
```


Хранение функции в словаре:

```python

def plus(num1, num2):
    return num1 + num2

def minus(num1, num2):
    return num1 - num2

operations = {
    'plus': plus,
    'minus': minus
}

def operation(num1, num2, operations_dict, operation):
    return operations_dict[operation](num1, num2)
```

#### Функция Map

Функция map принимает два параметра: функцию и итератор, например, список.

`map` применяет к каждому элементу списка переданную функцию. 

```python
string_numbers = ['1', '2', '3', '4', '5']
new_list = list(map(int, string_numbers))
print(new_list)
#  [1, 2, 3, 4, 5, 6, 7]
```

Еще один пример:

```python
def my_square(num):
    return num ** 2

numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

squared_numbers = list(map(my_square, numbers))

print(squared_numbers)

# [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
```

## Практика

###### Задание 1
Написать 2 функции.
Первая функция будет принимать строку и приводить ее к нижнему регистру.

Вторая функция будет принимать строку и проводить ее к верхнему регистру.

После чего с помощью `map` применить ваши  функции к списку строк.
Отдельно каждую функцию к отдельному спику строк!

###### Задание 2
Написать функцию которая будет простое число возводить в квардрат.

Необходимо возвести в квадрат все простые числа в списке используя функцию `map`

###### Задание 3
Написать собственную реализацию функции `map`.


## ДЗ

###### Уровень 1:
Доделать задания по практике

###### Уровень 2:
Написать функцию которая принимает список.
Необходимо посчитать количество одинаковых элементов в списке.

###### Уровень 3:
Самостоятельно разобраться с функцией `zip` в Python.
Написать самостоятельно реализацию функции `zip`.



###### Полезные ссылки:

[ZIP](https://tproger.ru/translations/implementing-zip-list-comprehensions/)

[Map, Zip, Lambda](http://ninjaside.info/blog/ru/funkcii-map-i-zip-i-lambda-python/)