# *** Функции - кирпичики любой программы ***

# *** Функции - "фабрики", которые на вход принимают как-либо данные и на выход возвращают другие данные

# 1 вариант: Функция, которая на вход ничего не принимает и на выход ничего не возвращает

def func_1():
    print("Hello World!")

def func_2():
    name = "Anastasia"
    print(f"Hello {name}!")

# 2 вариант. Функция, которая принимает данные (имеет вход(-ы)), но ничего не возвращает

def func_3(argument_1, arg_2):
    result = argument_1 + arg_2
    print(result)
    

# Вызов функции с передачей параметров аргументам
# func_3(100, 55)

# 2.1 Вариант. Функция, принимающая данные, но ничего не возвращает, причем аргументы имеют значения по умолчанию

def func_4(arg_1, arg_2, arg_3=10):
    result = arg_1 + arg_2 * arg_3
    print(result)

# func_4(100, 20, 5)


# 2.2 Вариант. Функция, принимающая данные, но ничего не возвращает
# позиционные параметры 

def func_5(arg_1, arg_2, arg_3):
    result = arg_1 + arg_2 * arg_3
    print(result)

# func_5(2,3,4)
# func_5(100,20)

# 2.3 Вариант. Функция, принимающая данные, но ничего не возвращает
# именованные параметры 

def func_6(arg_1=10, arg_2=20, arg_3=30):
    result = arg_1 + arg_2 * arg_3
    print(result)

# func_6(arg_3=100, arg_1=5)

# 2.4 Вариант. Функция, принимающая данные, но ничего не возвращает
# множественные позиционные параметры

def func_7(*args):
    """
    docstring
    """
    print(args)
# можно передавать произвольное количество позиционных параметров
# они будут упакованы в виде кортежа
# func_7(10,29,30)

def func_8(*args):
    """
    функция, которая складывает произвольное количество параметров
    """
    result = 0
    for num in args:
        result += num
    print(result)

# func_8(100, 200)

# 2.4 Вариант. Функция, принимающая данные, но ничего не возвращает
# множественные именованные параметры **

# def func_9(**args):
#     print(args)

# можно передавать произвольное количество именованных параметров
# и они будут упакованы в виде словаря
# func_9(x=100, y=200, z=300)
import math
# def distance_calculate(**args):
#     """
#     функция, которая вычисляет дистанцию от начала координат (0,0,0) до точки с координатами (x,y,z)
#     """
#     # переменная keys будет хранить список ключей аргументов из args
#     keys = list(args.keys())
    
#     # для вычисления дистанции применяем теорему Пифагора
#     summa = 0
#     # извлекаем все ключи из списка ключей аргументов из args
#     for axis in keys:
#         #суммирование квадратов катетов (значения координатных осей)
#         summa += args[axis] ** 2
#     distance = math.sqrt(summa)

#     print(f"Дистанция: {distance}")

# вызов функции
# distance_calculate(x=1, y=1, z=1, a=5)

# данные человека

# def person_data_parser(**args):
#     for parameter, value in args.items():
#         print(f"{parameter} : {value}")
# person_data_parser(name="John", age=32, prog_lang'Python')
# person_data_parser(name"Mike", age=25)

# вариант 3. Функция, которая возвращает значение

# определение (создание) функции, которая возвращает значение переменной z
def func_9(x,y):
    z = x+y
    return z

# вызов функции
result = func_9(10,20)
# print(result)
# возвращение нескольких значений

def func_10(a, b):
    res_1 = a ** 2
    res_2 = b ** 3
    return res_1, res_2
# print(func_10(2,3))

val_1, val_2 = func_10(2,3)

# print(val_1, val_2)

# *** Безымянная функция (лямбда-выраженияб лямбда-фукнции)

# Создание лямбды
foo = lambda x,y: (x+y)**2
# вызов лямбда выражения
result = foo(2,3)

# print(result)

# Лямбда внутри списка

# my_lambdas = [lambda arg: arg ** i for i in range(5)]

my_lambdas = [lambda arg: arg ** 2, lambda arg: arg ** 3, lambda arg: arg ** 4]

# print(my_lambdas)
#вызов лямбды из списка
# print(my_lambdas[1](2))

# лямбда внутри словаря

lambda_dict = {"вычисление суммы": lambda x, y: x+y, "вычисление произведения": lambda x, y: x*y}

# print(lambda_dict["вычисление суммы"](2,4))

# лямбда внутри генератора списка

# создание списка
my_list = [(lambda a: a*2)(n) for n in range(10)]

my_list_2 = [(lambda a, b: (a+b)*(a-b))(idx, val) for idx, val in enumerate([10,20,30,40,50])]
# создание кастомизированного списка на основе другого списка
origin_list = [3,1,7,4,7,2]
# лямбда с тернарным условным выражением
my_list_3 = [(lambda x: 1 if x >5 else 0)(n) for n in origin_list ]
# print(my_list_3)

# *** Декоратор ***

# декоратор - один из видов паттернов (шаблон) программирования (проектирование)
# декоратор - функция, которая добавляет некий доп.функционал целевой функции

# создание декоратора

def decorator_1(func):
    '''
    func : аргумент, которому присваивается объект целевой функции
    '''
    # обертка
    def wrapper():
        # код, который выполняется до целевой функции
        print("foo") 

        # выполнение целевой функции
        func()

        # может быть код, который выполняется после целевой функции
        print("bar")
    
    # хотим отправаить возврат объекта обертки
    return wrapper

# целевая функция
@decorator_1
def my_func_1():
    print("Hello!")

def my_func_2():
    print("Привет!")

# my_func_1()
# my_func_2()

def decorator_2(func):
    '''
    func : аргумент, которому присваивается объект целевой функции
    '''
    # обертка с аргументами
    def wrapper(a, b):
        # код, который выполняется до целевой функции
        print("foo") 

        # выполнение целевой функции с передачей параметров
        func(a, b)

        # может быть код, который выполняется после целевой функции
        print("bar")
    
    # хотим отправаить возврат объекта обертки
    return wrapper

@decorator_2
def my_func_3(x, y):
    print(x+y)

my_func_3(10, 5)
7f559b484ede65ab1a8ea6f9ee75e9904af8c879
def func_1():
    print('Hello World!')


def func_2():
    name = 'Anastasia'
    print(f'Hello {name}!')


def func_3(argument_1, arg_2):
    result = argument_1 + arg_2
    print(result)


def func_4(arg_1, arg_2, arg_3=10):
    result = arg_1 + arg_2 * arg_3
    print(result)


def func_5(arg_1, arg_2, arg_3):
    result = arg_1 + arg_2 * arg_3
    print(result)


<mask token>


def func_7(*args):
    """
    docstring
    """
    print(args)


def func_8(*args):
    """
    функция, которая складывает произвольное количество параметров
    """
    result = 0
    for num in args:
        result += num
    print(result)


<mask token>


def decorator_1(func):
    """
    func : аргумент, которому присваивается объект целевой функции
    """

    def wrapper():
        print('foo')
        func()
        print('bar')
    return wrapper


@decorator_1
def my_func_1():
    print('Hello!')


def my_func_2():
    print('Привет!')


def decorator_2(func):
    """
    func : аргумент, которому присваивается объект целевой функции
    """

    def wrapper(a, b):
        print('foo')
        func(a, b)
        print('bar')
    return wrapper


@decorator_2
def my_func_3(x, y):
    print(x + y)


<mask token>

def func_1():
    print('Hello World!')


def func_2():
    name = 'Anastasia'
    print(f'Hello {name}!')


def func_3(argument_1, arg_2):
    result = argument_1 + arg_2
    print(result)


def func_4(arg_1, arg_2, arg_3=10):
    result = arg_1 + arg_2 * arg_3
    print(result)


def func_5(arg_1, arg_2, arg_3):
    result = arg_1 + arg_2 * arg_3
    print(result)


<mask token>


def func_7(*args):
    """
    docstring
    """
    print(args)


def func_8(*args):
    """
    функция, которая складывает произвольное количество параметров
    """
    result = 0
    for num in args:
        result += num
    print(result)


<mask token>


def func_10(a, b):
    res_1 = a ** 2
    res_2 = b ** 3
    return res_1, res_2


<mask token>


def decorator_1(func):
    """
    func : аргумент, которому присваивается объект целевой функции
    """

    def wrapper():
        print('foo')
        func()
        print('bar')
    return wrapper


@decorator_1
def my_func_1():
    print('Hello!')


def my_func_2():
    print('Привет!')


def decorator_2(func):
    """
    func : аргумент, которому присваивается объект целевой функции
    """

    def wrapper(a, b):
        print('foo')
        func(a, b)
        print('bar')
    return wrapper


@decorator_2
def my_func_3(x, y):
    print(x + y)


<mask token>

def func_1():
    print('Hello World!')


def func_2():
    name = 'Anastasia'
    print(f'Hello {name}!')


def func_3(argument_1, arg_2):
    result = argument_1 + arg_2
    print(result)


def func_4(arg_1, arg_2, arg_3=10):
    result = arg_1 + arg_2 * arg_3
    print(result)


def func_5(arg_1, arg_2, arg_3):
    result = arg_1 + arg_2 * arg_3
    print(result)


def func_6(arg_1=10, arg_2=20, arg_3=30):
    result = arg_1 + arg_2 * arg_3
    print(result)


def func_7(*args):
    """
    docstring
    """
    print(args)


def func_8(*args):
    """
    функция, которая складывает произвольное количество параметров
    """
    result = 0
    for num in args:
        result += num
    print(result)


<mask token>


def func_9(x, y):
    z = x + y
    return z


<mask token>


def func_10(a, b):
    res_1 = a ** 2
    res_2 = b ** 3
    return res_1, res_2


<mask token>


def decorator_1(func):
    """
    func : аргумент, которому присваивается объект целевой функции
    """

    def wrapper():
        print('foo')
        func()
        print('bar')
    return wrapper


@decorator_1
def my_func_1():
    print('Hello!')


def my_func_2():
    print('Привет!')


def decorator_2(func):
    """
    func : аргумент, которому присваивается объект целевой функции
    """

    def wrapper(a, b):
        print('foo')
        func(a, b)
        print('bar')
    return wrapper


@decorator_2
def my_func_3(x, y):
    print(x + y)


<mask token>

def func_1():
    print('Hello World!')


def func_2():
    name = 'Anastasia'
    print(f'Hello {name}!')


def func_3(argument_1, arg_2):
    result = argument_1 + arg_2
    print(result)


def func_4(arg_1, arg_2, arg_3=10):
    result = arg_1 + arg_2 * arg_3
    print(result)


def func_5(arg_1, arg_2, arg_3):
    result = arg_1 + arg_2 * arg_3
    print(result)


def func_6(arg_1=10, arg_2=20, arg_3=30):
    result = arg_1 + arg_2 * arg_3
    print(result)


def func_7(*args):
    """
    docstring
    """
    print(args)


def func_8(*args):
    """
    функция, которая складывает произвольное количество параметров
    """
    result = 0
    for num in args:
        result += num
    print(result)


<mask token>


def func_9(x, y):
    z = x + y
    return z


<mask token>


def func_10(a, b):
    res_1 = a ** 2
    res_2 = b ** 3
    return res_1, res_2


<mask token>


def decorator_1(func):
    """
    func : аргумент, которому присваивается объект целевой функции
    """

    def wrapper():
        print('foo')
        func()
        print('bar')
    return wrapper


@decorator_1
def my_func_1():
    print('Hello!')


def my_func_2():
    print('Привет!')


def decorator_2(func):
    """
    func : аргумент, которому присваивается объект целевой функции
    """

    def wrapper(a, b):
        print('foo')
        func(a, b)
        print('bar')
    return wrapper


@decorator_2
def my_func_3(x, y):
    print(x + y)


my_func_3(10, 5)

# *** Функции - кирпичики любой программы ***

# *** Функции - "фабрики", которые на вход принимают как-либо данные и на выход возвращают другие данные

# 1 вариант: Функция, которая на вход ничего не принимает и на выход ничего не возвращает

def func_1():
    print("Hello World!")

def func_2():
    name = "Anastasia"
    print(f"Hello {name}!")

# 2 вариант. Функция, которая принимает данные (имеет вход(-ы)), но ничего не возвращает

def func_3(argument_1, arg_2):
    result = argument_1 + arg_2
    print(result)
    

# Вызов функции с передачей параметров аргументам
# func_3(100, 55)

# 2.1 Вариант. Функция, принимающая данные, но ничего не возвращает, причем аргументы имеют значения по умолчанию

def func_4(arg_1, arg_2, arg_3=10):
    result = arg_1 + arg_2 * arg_3
    print(result)

# func_4(100, 20, 5)


# 2.2 Вариант. Функция, принимающая данные, но ничего не возвращает
# позиционные параметры 

def func_5(arg_1, arg_2, arg_3):
    result = arg_1 + arg_2 * arg_3
    print(result)

# func_5(2,3,4)
# func_5(100,20)

# 2.3 Вариант. Функция, принимающая данные, но ничего не возвращает
# именованные параметры 

def func_6(arg_1=10, arg_2=20, arg_3=30):
    result = arg_1 + arg_2 * arg_3
    print(result)

# func_6(arg_3=100, arg_1=5)

# 2.4 Вариант. Функция, принимающая данные, но ничего не возвращает
# множественные позиционные параметры

def func_7(*args):
    """
    docstring
    """
    print(args)
# можно передавать произвольное количество позиционных параметров
# они будут упакованы в виде кортежа
# func_7(10,29,30)

def func_8(*args):
    """
    функция, которая складывает произвольное количество параметров
    """
    result = 0
    for num in args:
        result += num
    print(result)

# func_8(100, 200)

# 2.4 Вариант. Функция, принимающая данные, но ничего не возвращает
# множественные именованные параметры **

# def func_9(**args):
#     print(args)

# можно передавать произвольное количество именованных параметров
# и они будут упакованы в виде словаря
# func_9(x=100, y=200, z=300)
import math
# def distance_calculate(**args):
#     """
#     функция, которая вычисляет дистанцию от начала координат (0,0,0) до точки с координатами (x,y,z)
#     """
#     # переменная keys будет хранить список ключей аргументов из args
#     keys = list(args.keys())
    
#     # для вычисления дистанции применяем теорему Пифагора
#     summa = 0
#     # извлекаем все ключи из списка ключей аргументов из args
#     for axis in keys:
#         #суммирование квадратов катетов (значения координатных осей)
#         summa += args[axis] ** 2
#     distance = math.sqrt(summa)

#     print(f"Дистанция: {distance}")

# вызов функции
# distance_calculate(x=1, y=1, z=1, a=5)

# данные человека

# def person_data_parser(**args):
#     for parameter, value in args.items():
#         print(f"{parameter} : {value}")
# person_data_parser(name="John", age=32, prog_lang'Python')
# person_data_parser(name"Mike", age=25)

# вариант 3. Функция, которая возвращает значение

# определение (создание) функции, которая возвращает значение переменной z
def func_9(x,y):
    z = x+y
    return z

# вызов функции
result = func_9(10,20)
# print(result)
# возвращение нескольких значений

def func_10(a, b):
    res_1 = a ** 2
    res_2 = b ** 3
    return res_1, res_2
# print(func_10(2,3))

val_1, val_2 = func_10(2,3)

# print(val_1, val_2)

# *** Безымянная функция (лямбда-выраженияб лямбда-фукнции)

# Создание лямбды
foo = lambda x,y: (x+y)**2
# вызов лямбда выражения
result = foo(2,3)

# print(result)

# Лямбда внутри списка

# my_lambdas = [lambda arg: arg ** i for i in range(5)]

my_lambdas = [lambda arg: arg ** 2, lambda arg: arg ** 3, lambda arg: arg ** 4]

# print(my_lambdas)
#вызов лямбды из списка
# print(my_lambdas[1](2))

# лямбда внутри словаря

lambda_dict = {"вычисление суммы": lambda x, y: x+y, "вычисление произведения": lambda x, y: x*y}

# print(lambda_dict["вычисление суммы"](2,4))

# лямбда внутри генератора списка

# создание списка
my_list = [(lambda a: a*2)(n) for n in range(10)]

my_list_2 = [(lambda a, b: (a+b)*(a-b))(idx, val) for idx, val in enumerate([10,20,30,40,50])]
# создание кастомизированного списка на основе другого списка
origin_list = [3,1,7,4,7,2]
# лямбда с тернарным условным выражением
my_list_3 = [(lambda x: 1 if x >5 else 0)(n) for n in origin_list ]
# print(my_list_3)

# *** Декоратор ***

# декоратор - один из видов паттернов (шаблон) программирования (проектирование)
# декоратор - функция, которая добавляет некий доп.функционал целевой функции

# создание декоратора

def decorator_1(func):
    '''
    func : аргумент, которому присваивается объект целевой функции
    '''
    # обертка
    def wrapper():
        # код, который выполняется до целевой функции
        print("foo") 

        # выполнение целевой функции
        func()

        # может быть код, который выполняется после целевой функции
        print("bar")
    
    # хотим отправаить возврат объекта обертки
    return wrapper

# целевая функция
@decorator_1
def my_func_1():
    print("Hello!")

def my_func_2():
    print("Привет!")

# my_func_1()
# my_func_2()

def decorator_2(func):
    '''
    func : аргумент, которому присваивается объект целевой функции
    '''
    # обертка с аргументами
    def wrapper(a, b):
        # код, который выполняется до целевой функции
        print("foo") 

        # выполнение целевой функции с передачей параметров
        func(a, b)

        # может быть код, который выполняется после целевой функции
        print("bar")
    
    # хотим отправаить возврат объекта обертки
    return wrapper

@decorator_2
def my_func_3(x, y):
    print(x+y)

my_func_3(10, 5)