Day 54 & 55 - Function Decorators


Posted by pei_______ on 2022-06-03

learning from 100 Days of Code: The Complete Python Pro Bootcamp for 2022


Real Python - Primer on Python Decorators


目錄

一、Decorators

  1. 前導概念 first class function
  2. 前導概念 Closures
  3. Decorators

二、Advanced in Decorators

  1. Decorators in Flask
  2. Decorators on def. function
  3. Decorators with Class

三、Higher-Lower Game


一、Decorators


1. 前導概念 first class function:

wiki上的定義: a programming language is said to have first-class functions if it treats functions as first-class citizens.This means the language supports passing functions as arguments to other functions, returning them as the values from other functions, and assigning them to variables or storing them in data structures

簡而言之

  1. 函式被當成一級公民對待,可以被當成參數回傳或使用
  2. 回調函式 ( Callback function ) - 被當作參數傳遞的那個函式
  3. 高階函式 ( Higher-Order Function ) - 當一個函式可以回傳另一個函式
# eg.1

def logger(msg):

    def log_message():
        print('Log:', msg)

    return log_message

log_hi = logger('Hi!')
log_hi()


> Log: Hi

'''
1. 高階函式: logger
2. 回傳函式: log_message
3. 接收函式與內容:
    msg = 'Hi!'
    log_hi = log_message {
        print('Log:', 'Hi!')
    }
4. 因高階函式內無呼叫log_message(),故只會return log_massage
5. 呼叫log_hi()才會印出'Log: Hi'
'''
# eg.2

def html_tag(tag):

    def wrap_text(msg):
        print(f'<{tag}>{msg}</{tag}>')

    return wrap_text

print_h1 = html_tag('h1')
print_h1('Test Headline!')
print_h1('Another Headline!')

print_p = html_tag('p')
print_p('Test Paragraph!')


> <h1>Test Headline!</h1>
> <h1>Another Headline!</h1>
> <p>Test Paragraph!</p>

'''
1. 高階函式: html_tag(tag)
2. 回傳函式: wrap_text(msg)
3. 接收函式與內容:

print_h1 = wrap_text(msg) {
    print(f"<h1>{msg}</h1>")
}

print_p = wrap_text(msg) {
    print(f"<p>{msg}</p>")
}
'''

2. 前導概念 Closures

  • return inner function with local variable
# eg.1

def logger(func):

    def log_func(*args):

        print(f"Running {func.__name__} with argument {args}")
        prinr(func(*args))

    return log_func

def add(x, y):
    return x+y

def sub(x, y):
    return x-y

add_log = logger(add)
add_log(5, 3)

sub_log = logger(sub)
sub_log(5, 3)

> 8
> 2

'''
1. 接收函式與內容:
add_log = log_func(*args){
    print(f"Running {add} with argument {args}")
    prinr(func(*args))
}

sub_log = log_func(*args){
    print(f"Running {sub} with argument {args}")
    prinr(func(*args))
}
2. 
'''

3. Decorators: 以高階函式包裝回傳函式

# eg.1 回傳函數不需給予參數

def double_decorator(function):

    def wrapper_function():
        # Do something before
        function()
        function()
        # Do something after

    return wrapper_function


#---  without decorators ---#

def say_bye():
    print("Bye")

decorated_function = double_decorator(say_bye)
decorated_function()


#--- with decorators ---#

@double_decorator
def say_hello():
    print("Hello")

say_hello()

'''
@double_decorator say_hello() 

equal:
say_hello() = double_decorator(say_hello())
'''

# eg.2 回傳函數需要給予參數
# 可統一設定 *args & **kwargs: allow any position args or keyword args 

def decorator_function(original_function):

    def wrapper_function(*args, **kwargs):
        print(f'wrapper executed this before {original_function.__name__}')
        return original_function(*args, **kwargs)

    return wrapper_function


#---  without decorators ---#

def diplay():
    print('display function ran')

decorator_display = decorator_function(display)

decorator_display()


#--- with decorators ---#

@decorator_function
def display():
    print('display function ran')

display()

'''
@decorator_function display()

equal:
display = decorator_function(display)
'''

# eg.3

import time
current_time = time.time()

def speed_calc_decorator(speed_function):

    def wrapper_function():
        speed_function()
        after_loop_time = time.time()
        time_different = after_loop_time - current_time
        print(f"{speed_function.__name__} run speed: {time_different}s")

    return wrapper_function

@speed_calc_decorator
def fast_function():
    for i in range(10000000):
        i * i

@speed_calc_decorator
def slow_function():
    for i in range(100000000):
        i * i

fast_function()
slow_function()

'''
@speed_calc_decorator fast_function() = speed_calc_decorator(fast_function())
@speed_calc_decorator slow_function() = speed_calc_decorator(slow_function())
'''

二、Advanced in Decorators


1. Decorators in Flask

def make_bold(response_function):
    def font_style():
        return f"<b>{response_function()}</b>"
    return font_style

def make_emphasis(response_function):
    def font_style():
        return f"<em>{response_function()}</em>"
    return font_style

def make_underlined(response_function):
    def font_style():
        return f"<u>{response_function()}</u>"
    return font_style


@app.route('/bye')
@make_bold
@make_emphasis
@make_underlined
def bye_world():
    return 'Bye'

'''
bold_bye = make_bold(bye_word) = font_style(bye_word)
bold_bye = make_emphasis(bye_word) = font_style(bye_word)
bold_bye = make_underlined(bye_word) = font_style(bye_word)
'''

2. Decorators on def. function

def logging_decorator(func):
    def wrapper(*args):
        print(f"You called {func.__name__}{args}")
        print(f"It returned: {func(args)}")

    return wrapper

@logging_decorator
def a_fucn(*args):
    for num in args:
        return sum(num)

a_fucn(1,2,3)

3. Decorators with Class

class User:
  def __init__(self, name):
    self.name = name
    self.is_login = False


def is_authenticated(function):
  def  wrapper(*args, **kwargs):
    if args[0].is_login == True:
      function(args[0])
  return wrapper

@is_authenticated
def creat_blog_post(user):
  print(f"{user.name} have create a post.")


new_user = User("Penny")
new_user.is_login = True
creat_blog_post(new_user)

三、Higher-Lower Game

from flask import Flask
from random import randint
app = Flask(import_name=__name__)

ANSWER = int()


def check_answer(web_function):
    def wrapper(**kwargs):
        global ANSWER
        if kwargs['num'] > ANSWER:
            return "<h1 style='color: purple'>Too High, try again!</h1>" \
                   "<img src='https://media.giphy.com/media/3o6ZtaO9BZHcOjmErm/giphy.gif'>"

        elif kwargs['num'] < ANSWER:
            return "<h1 style='color: red'>Too Low, try again!</h1>" \
                   "<img src='https://media.giphy.com/media/jD4DwBtqPXRXa/giphy.gif'>"
        else:
            return "<h1 style='color: green'>You found me!</h1>" \
                   "<img src='https://media.giphy.com/media/4T7e4DmcrP9du/giphy.gif'>"
    return wrapper


@app.route('/')
def home_page():
    global ANSWER
    ANSWER = randint(0, 9)
    # print(ANSWER)
    return f"<h1>Guess a number between 0 and 9</h1>" \
           f"<img src='https://media.giphy.com/media/3o7aCSPqXE5C6T8tBC/giphy.gif'>" \
           f"<h3>HINT: You will get different answer if you come back to this page</h3>"


@app.route('/<int:num>')
@check_answer
def num_page(num):
    return num


if __name__ == "__main__":
    app.run(debug=True)

#Python #課堂筆記 #100 Days of Code







Related Posts

[BE201] 後端中階:ORM 與 Sequelize

[BE201] 後端中階:ORM 與 Sequelize

兩場後端新手工程師講座筆記

兩場後端新手工程師講座筆記

Whack A Mole Game

Whack A Mole Game


Comments