diff --git a/README.md b/README.md
index 7e5b77e..d161424 100644
--- a/README.md
+++ b/README.md
@@ -2581,12 +2581,135 @@ def sum(a, b):
Explain what is a decorator
-
+
+In python, everything is an object, even functions themselves. Therefore you could pass functions as arguments
+for another function eg;
+
+```
+def wee(word):
+ return word
+
+def oh(f):
+ return f + "Ohh"
+
+>>> oh(wee("Wee"))
+<<< Wee Ohh
+```
+
+This allows us to control the before execution of any given function and if we added another function as wrapper,
+(a function receiving another function that receives a function as parameter) we could also control the after execution.
+
+Sometimes we want to control the before-after execution of many functions and it would get tedious to write
+
+ f = function(function_1())
+ f = function(function_1(function_2(*args)))
+
+every time, that's what decorators do, they introduce syntax to write all of this on the go, using the keyword '@'.
+
+
Can you show how to write and use decorators?
-
+
+These two decorators (ntimes and timer) are usually used to display decorators functionalities, you can find them in lots of
+tutorials/reviews. I first saw these examples two years ago in pyData 2017. https://www.youtube.com/watch?v=7lmCu8wz8ro&t=3731s
+
+```
+Simple decorator:
+
+def deco(f):
+ print(f"Hi I am the {f.__name__}() function!")
+ return f
+
+@deco
+def hello_world():
+ return "Hi, I'm in!"
+
+a = hello_world()
+print(a)
+
+>>> Hi I am the hello_world() function!
+ Hi, I'm in!
+```
+
+This is the simplest decorator version, it basically saves us from writting a = deco(hello_world())
.
+But at this point we can only control the before execution, let's take on the after:
+
+```
+def deco(f):
+ def wrapper(*args, **kwargs):
+ print("Rick Sanchez!")
+ func = f(*args, **kwargs)
+ print("I'm in!")
+ return func
+ return wrapper
+
+@deco
+def f(word):
+ print(word)
+
+a = f("************")
+>>> Rick Sanchez!
+ ************
+ I'm in!
+```
+
+deco receives a function -> f
+wrapper receives the arguments -> *args, **kwargs
+
+wrapper returns the function plus the arguments -> f(*args, **kwargs)
+deco returns wrapper.
+
+As you can see we conveniently do things before and after the execution of a given function.
+
+For example, we could write a decorator that calculates the execution time of a function.
+
+```
+import time
+def deco(f):
+ def wrapper(*args, **kwargs):
+ before = time.time()
+ func = f(*args, **kwargs)
+ after = time.time()
+ print(after-before)
+ return func
+ return wrapper
+
+@deco
+def f():
+ time.sleep(2)
+ print("************")
+
+a = f()
+>>> 2.0008859634399414
+```
+
+Or create a decorator that executes a function n times.
+
+```
+def n_times(n):
+ def wrapper(f):
+ def inner(*args, **kwargs):
+ for _ in range(n):
+ func = f(*args, **kwargs)
+ return func
+ return inner
+ return wrapper
+
+@n_times(4)
+def f():
+ print("************")
+
+a = f()
+
+>>>************
+ ************
+ ************
+ ************
+```
+
+
Write a script which will determine if a given host is accessible on a given port