<jupyter_start><jupyter_text>Defining functions inside other functions<jupyter_code>def plus_one(number):
    def add_one(number):
        return number+1
    result = add_one(number)
    return result
plus_one(4)<jupyter_output><empty_output><jupyter_text>Passing functions as arguments to other functions<jupyter_code>def plus_one(number):
    return number + 1

def function_call(number, function):
    # number = 5
    return function(number)

function_call(5, plus_one)<jupyter_output><empty_output><jupyter_text>Closure Pattern<jupyter_code>def print_message(message):
    def message_sender():
        print(message)
    return message_sender

decorate = print_message("some random message")
decorate()<jupyter_output>some random message<jupyter_text>Creating Decorators<jupyter_code>def uppercase_decorator(function):
    def wrapper():
        func = function()
        make_uppercase = func.upper()
        return make_uppercase
    
    return wrapper

def say_hi():
    return 'hello there'

decorate = uppercase_decorator(say_hi)
decorate()<jupyter_output><empty_output><jupyter_text>Using @symbol<jupyter_code>@uppercase_decorator
def say_hi():
    return 'hello there'

say_hi()
def split_string(function):
    def wrapper():
        func = function()
        split_string = func.split()
        return split_string
    
    return wrapper

@split_string
@uppercase_decorator
def say_hi():
    return 'hello there'
    
say_hi()<jupyter_output><empty_output><jupyter_text>Simple Decorators<jupyter_code>def decorator(func):
    def wrapper():
        print(f'this is decorated message before input functions')
        func()
        print(f'this is decorated message after input functions')
    return wrapper

def say_hi():
    print("hi")

say = decorator(say_hi)
say()<jupyter_output>this is decorated message before input functions
hi
this is decorated message after input functions<jupyter_text>Syntactic Sugar<jupyter_code>@decorator
def say_hi():
    print("say hi")

say_hi()<jupyter_output>this is decorated message before input functions
say hi
this is decorated message after input functions<jupyter_text>Reusing Decorators<jupyter_code>def do_twice(func):
    def wrapper():
        func()
        func()
    return wrapper

@do_twice
def say_hi():
    print("say hi")

say_hi()<jupyter_output>say hi
say hi<jupyter_text>Decorating Functions with Arguments<jupyter_code>@do_twice
def greet(name):
    print(f'hello {name}')

greet('world')
def do_twice(func):
    def wrapper_do_twice(*args, **kwargs):
        func(*args, **kwargs)
        func(*args, **kwargs)
    return wrapper_do_twice

@do_twice
def greet(name):
    print(f'hello {name}')

@do_twice
def say_hi():
    print(f'hi')

greet('world')
say_hi()
print(say_hi.__name__)
help(say_hi)
import functools

def do_twice(func):
    @functools.wraps(func)
    def wrapper_do_twice(*args, **kwargs):
        func(*args, **kwargs)
        return func(*args, **kwargs)
    return wrapper_do_twice

@do_twice
def say_hi():
    print(f'hi')

print(say_hi.__name__)
help(say_hi)<jupyter_output>say_hi
Help on function say_hi in module __main__:

say_hi()<jupyter_text>Timing Functions<jupyter_code>import functools
import time

def timer(func):
    """Print the runtime of the decorated function"""
    @functools.wraps(func)
    def wrapper_timer(*args, **kwargs):
        start_time = time.perf_counter()    # 1
        value = func(*args, **kwargs)
        end_time = time.perf_counter()      # 2
        run_time = end_time - start_time    # 3
        print(f"Finished {func.__name__!r} in {run_time:.4f} secs")
        return value
    return wrapper_timer

@timer
def waste_some_time(num_times):
    for _ in range(num_times):
        sum([i**2 for i in range(10000)])
waste_some_time(1)
waste_some_time(999)<jupyter_output>Finished 'waste_some_time' in 1.6767 secs<jupyter_text>Debugging Code<jupyter_code>import functools

def debug(func):
    """Print the function signature and return value"""
    @functools.wraps(func)
    def wrapper_debug(*args, **kwargs):
        args_repr = [repr(a) for a in args]                      # 1
        kwargs_repr = [f"{k}={v!r}" for k, v in kwargs.items()]  # 2
        signature = ", ".join(args_repr + kwargs_repr)           # 3
        print(f"Calling {func.__name__}({signature})")
        value = func(*args, **kwargs)
        print(f"{func.__name__!r} returned {value!r}")           # 4
        return value
    return wrapper_debug
@debug
def make_greeting(name, age=None):
    if age is None:
        return f"Howdy {name}!"
    else:
        return f"Whoa {name}! {age} already, you are growing up!"
print(make_greeting('Benjamin'))
print(make_greeting('Iggy',age=30))
import math

# Apply a decorator to a standard library function
math.factorial = debug(math.factorial)

def approximate_e(terms=18):
    return sum(1 / math.factorial(n) for n in range(terms))
approximate_e(10)
import random
PLUGINS = dict()

def register(func):
    """Register a function as a plug-in"""
    PLUGINS[func.__name__] = func
    return func

@register
def say_hello(name):
    return f"Hello {name}"

@register
def be_awesome(name):
    return f"Yo {name}, together we are the awesomest!"

def randomly_greet(name):
    greeter, greeter_func = random.choice(list(PLUGINS.items()))
    print(f"Using {greeter!r}")
    return greeter_func(name)
print(PLUGINS)
randomly_greet('alice')<jupyter_output>{'say_hello': <function say_hello at 0x7f8cb4c5d158>, 'be_awesome': <function be_awesome at 0x7f8cb4c5d0d0>}
Using 'be_awesome'<jupyter_text>Decorating Classes  @staticmethod<jupyter_code>class Calculator:
    @staticmethod
    def addNumbers(x,y):  #self is not as input argument
        return x+y

print(Calculator.addNumbers(2,5))
c = Calculator
print(c.addNumbers(2,3))<jupyter_output>7
5<jupyter_text>@property<jupyter_code>class C(object):
    def __init__(self):
        self._x = None
 
    def getx(self):
        return self._x
 
    def setx(self, value):
        self._x = value
 
    def delx(self):
        del self._x
 
    x = property(getx, setx, delx, "I'm the 'x' property.")
print(C.x)
c = C
print(c.x)
c.x = 5
print(c.x)
class C(object):
    def __init__(self):
        self._x = None
 
    @property
    def x(self):
        """I'm the 'x' property."""
        return self._x
 
    @x.setter
    def x(self, value):
        self._x = value
 
    @x.deleter
    def x(self):
        del self._x
c= C
print(c.x)
c.x = 10
print(c.x)
del c.x
print(c.x)
class C(object):
    def __init__(self):
        self._x = None
 
    @property
    def x(self):
        """I'm the 'x' property."""
        return self._x
 
    @x.setter
    def x(self, value):
        self._x = value
 
    @x.deleter
    def x(self):
        del self._x
c= C
print(c.x)
c.x = 10
print(c.x)
del c.x
print(c.x)<jupyter_output><property object at 0x7f8c9f8402c8>
10<jupyter_text>Nested Decorators  Decorators with Arguments<jupyter_code>import functools

def repeat(num_times):
    def repeat_decorator(func):
        @functools.wraps(func)
        def wrapper_repeat(*args, **kwargs):
            for _ in range(num_times):
                func(*args, **kwargs)
            return func(*args, **kwargs)
        return wrapper_repeat
    return repeat_decorator

@repeat(num_times=4)
def say_hi(name):
    print(f'hi {name}')

print(say_hi.__name__)
help(say_hi)
say_hi('world')<jupyter_output>say_hi
Help on function say_hi in module __main__:

say_hi(name)

hi world
hi world
hi world
hi world
hi world<jupyter_text>Stateful Decorators<jupyter_code>import functools

def count_calls(func):
    @functools.wraps(func)
    def wrapper_count_calls(*args, **kwargs):
        wrapper_count_calls.num_calls += 1
        print(f"Call {wrapper_count_calls.num_calls} of {func.__name__!r}")
        return func(*args, **kwargs)
    wrapper_count_calls.num_calls = 0
    return wrapper_count_calls

@count_calls
def say_whee():
    print("Whee!")

say_whee()
say_whee()<jupyter_output>Call 2 of 'say_whee'
Whee!
{'fraction_non_alphanumeric': 0.11613386613386613, 'fraction_numerical': 0.00949050949050949, 'mean_word_length': 3.7531157270029674, 'pattern_counts': {'":': 0, '<': 53, '<?xml version=': 0, '>': 53, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '15', 'dir': 'jupyter-structured-clean-dedup', 'id': '192053', 'license': 'MIT', 'n_tokens_mistral': 2907, 'n_tokens_neox': 2847, 'n_words': 646, 'path': 'python_basic/decorator.ipynb', 'repo_name': 'hadleyhzy34/data_structure_and_algorithm', 'size': '8008'}