from functools import wraps


def text_decor(func):
	def wrapper(*args):
		print("Hello")
		func(*args)
		print("Goodbye!")
	return wrapper

@text_decor
def simple_func():
    print('I just simple python func')

# simple_func()

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

def repeater(func):
	def wrapper(*args):
		func(*args)
		func(*args)
	return wrapper


@repeater
def multiply(num1, num2):
    print(num1 * num2)

# multiply(2, 7) # после этого распечатается две строки со значением 14
# multiply(5, 3) # после этого распечатается две строки со значением 15

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


def double_it(func):
	def wrapper(*args):
		return 2*func(*args)
	return wrapper

@double_it
def multiply(num1, num2):
    return num1 * num2

# res = multiply(9, 4) # произведение 9*4=36, но декоратор double_it удваивает это значение
# print(res)


#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


def memoize(func):
	cache = {}
	@wraps(func) 
	def wrapper(n):
		if n not in cache:
			cache[n] = func(n)
		return cache[n]
	return wrapper


@memoize
def fibonacci(n):
    """
    Возвращает n-ое число Фибоначчи
    """
    if n < 2:
        return n
    return fibonacci(n - 1) + fibonacci(n - 2)


print(fibonacci(50))
# assert fibonacci(50) == 12586269025