learning from 100 Days of Code: The Complete Python Pro Bootcamp for 2022
Real Python - Primer on Python Decorators
目錄
一、Decorators
- 前導概念 first class function
- 前導概念 Closures
- Decorators
二、Advanced in Decorators
- Decorators in Flask
- Decorators on def. function
- 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
簡而言之
- 函式被當成一級公民對待,可以被當成參數回傳或使用
- 回調函式 ( Callback function ) - 被當作參數傳遞的那個函式
- 高階函式 ( 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)