---
title: Python 學習筆記
tags: Note, Python
---
# 前言
###### tags: `Python` `flask`
此為用來紀錄學習Python的個人筆記,筆記內容不定時更新中...
:::spoiler **Python Learning Resource**
> * 書籍: https://automatetheboringstuff.com
> * Python docs: https://docs.python.org/3/
> * Another really good resource: [programiz](https://www.programiz.com/python-programming/first-program), [Snakify](https://snakify.org/en/)
> * [Setting up VSCode for Python]( https://www.youtube.com/watch?v=W--_EOzdTHk)
:::
<br/>
# Python 環境設置
在看各種專案開發教學時,常會看到建議使用**虛擬環境**來進行開發,那什麼是虛擬環境呢?虛擬環境為一種獨立的開發環境,隔離不同專案間所需要的套件版本,每個專案可以擁有自己的python版本和所需的套件,即使在同一台機器上運行也不會被影響。`virtualenv` 則是常用來建立虛擬環境的套件之一。
針對不同的專案建立不同虛擬環境有什麼優點呢?
能夠清楚了解各個專案究竟需要哪些module,後續在維護上,module在更新上也比較方便。不然光是看專案要用的module有哪些要更新、更新到什麼版本,module間的相依性,就要弄到瘋掉了😂
## Python 版本控制
Python的版本管理工具有很多,virtualenv, pyenv, anaconda等。
我目前是使用 `pyenv` ([官方文件](https://github.com/pyenv/pyenv#basic-github-checkout)),這個開源的Python 版本管理工具。
**安裝pyenv**
```bash
# Mac
brew install pyenv
# Windows or Linux
git clone https://github.com/pyenv/pyenv.git ~/.pyenv
```
**設定pyenv環境變數**
如果使用的shell是 bash,則在 `~/.bashrc` 添加下列路徑
如果用zsh則是在 `~/.zshrc`
```bash
echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.zshrc
echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.zshrc
echo -e 'if command -v pyenv 1>/dev/null 2>&1; then\n eval "$(pyenv init -)"\nfi' >> ~/.zshrc
```
添加完路徑後,輸入下列指令使文件的配置生效
```bash
# 使當前shell 配置生效
exec $SHELL
source ~/.zshrc
```
### 常用的`pyenv`指令
**查看pyenv可安裝python 版本**
```bash
pyenv install -l
```
**使用pyenv安裝特定版本python**
```
pyenv install -v 3.8.5
```
**查看過去pyenv 安裝過的python版本**
```
pyenv versions
```
**切換python版本**
```
pyenv global 3.8.5
pyenv local 3.8.5
pyenv shell 3.8.5
```
`global` , `local` , `shell` 差異:
- `global`: 對應全局
- `local`: 對應當前資料夾
- `shell`: 對應當前shell
優先順序: `shell` > `local` > `global`
## 虛擬環境安裝
如果已經安裝好python([Windows的Python 安裝教學](https://ithelp.ithome.com.tw/articles/10210071)),就可以直接使用 `pip3` (或 `pip` ) 來安裝。
**先將pip版本升到最新**
```bash
pip3 install — upgrade pip
```
**檢查目前pip有安裝什麼套件**
```bash
pip3 list
```
**安裝virtualenv**
```bash
# python2
pip install virtualenv
# python3
pip3 install virtualenv
```
安裝完後可以再下一次 `pip3 list` 確認有多個剛剛安裝的 `virtualenv` 套件。
**建立虛擬環境**
移動到專案資料夾內,並輸入以下指令。完成後可以在專案內看到多了個venv資料夾
```bash
# 使用環境預設python 版本
virtualenv venv
# venv是自設的資料夾名
# 指定python版本
virtualenv -p ~/.pyenv/versions/3.8.5/bin/python venv
```
**進入虛擬環境**
```bash
source venv/bin/activate
```
**離開虛擬環境**
```
deactivate
```
**匯出開發環境所需的所有套件**
```bash
pip freeze > requirements.txt
```
**從requirement.txt下載所有所需套件**
```bash
pip install -r requirements.txt
```
##### [參考資料]: [pip workflow 管理 requirements.txt](http://pre.tir.tw/008/blog/output/pip-workflow-guan-li-requirementtxt.html)
> 上述作法會有幾個缺點:
>
> 1. 不易升級所使用的module
> 2. 不易看出此專案所依賴的top dependencies
>
> 解決方法:維護兩份文件
>
> - `requirements-to-freeze.txt` : 紀錄專案所依賴的top-level dependencies
> - `requirements.txt` : 紀錄pip freeze的結果
>
> 用法:
>
> ```bash
> cd project-repo
> pip install -r requirements-to-freeze.txt --upgrade
> pip freeze > requirements.txt
> ```
<br/>
# Python Debugger
##### [參考資料]: [官方文件](https://docs.python.org/3/library/pdb.html)
<br/>
## 從terminal運行
可以在terminal使用Python Debugger執行一個腳本,舉例:
```bash
$ python -m pdb my_script.py
```
這會觸發debugger在腳本第一行指令處停止執行。
## 從腳本內部執行
也可以在腳本內部設置斷點,如此便可以在某些特定點看變數等各種執行的訊息。以下面例子而言,用 `pdb.set_tract()` 來設斷點:
```python
import pdb
def test():
pdb.set_trace()
return "test"
print(test())
```
如果執行上面這個腳本會發現,在執行時直接進入debugger模式。以下可以看到一些debugger模式常用的指令。
## Debugger常用指令
- `c`: 繼續執行
- `w`: 顯示目前正在執行的程式行的上下文訊息
- `a`: 印出當前function的參數列表
- `s`: (**s**tep)執行當前這行程式,並停在第一個能停的地方
- `n`: (**n**ext)繼續執行到當前function的下一行,或當前行直接返回(單步跳過)
---
# Flask - RESTful API server
##### [參考資料]: [your first API server](https://www.fernandomc.com/posts/your-first-flask-api/)
## What is Flask
1. 以Python實作的輕量級網站框架(micro framework),只實作了核心的網頁應用程式功能(包含routing等),將其他進階的功能(像是認證、資料庫ORM)交給擴充套件(extensions)。
2. Flask內建一個實作WSGI伺服器與路由功能的Werkzeug工具集。
> **WSGI(Web Server Gateway Interface)**
> 為Python定義網頁程式和伺服器溝通的介面,為Python網頁開發的標準。
> 將網頁元件分為三類:web server, web middleware, web application
## RESTful API
**Resource Representational State Transfer(REST)**
REST不是一種架構、協定,而是一種網路架構風格,由HTTP協定既有的幾種method (GET, POST etc.)轉換對server的各種操作,利用位置訊息來請求一份資源(URL)後,資源實體用(Json, XML等)形式表現出來。
REST = 資源 (Resource) + 表現層 (Representation) + 狀態轉化 (State Transfer)
## Introduction
API(Application Programming Interface) 作為server to server 或 client to server之間的溝通管道,可以使整體的系統利用API互相請求服務而不需知道對方實作細節,僅呼叫API介面即可完成功能。
API為何需要使用表現層狀態轉移(REST)的風格來設計呢?有以下的優點 [出自WIKI](https://en.wikipedia.org/wiki/Representational_state_transfer):
1. Client-Server
2. 無狀態(Stateless)
3. 可快取 (Cachable)
4. 分層系統 (Layered System)
5. 統一接口 (Uniform Interface)
## Basic Concept
**HTTP status code (狀態碼)**
| 狀態碼 | 描述 |
| ------ | -------------------------------------- |
| 2XX | 已接受請求正常處理並返回結果 |
| 3XX | 使用者代理須採取進一步動作,以完成請求 |
| 4XX | 客戶端,請求有錯誤 |
| 5XX | 伺服器端,內部有錯誤 |
**Paging and Filtering**
若要回傳一大串集合或大量資料,可以提供分頁(Paging) 和過濾(Filtering)等機制,以增加API的效率和可用性。
- Paging
`[GET]/rest/product?start=2&limit=3`
- Filtering
`[GET]/rest/product?introduction=iphone`
**基礎架構**
用基礎的三層式架構 (Three-tier architecture)來開發RESTful API server。
##### [參考資料]: [3-Layer 基礎架構](https://dotblogs.com.tw/clark/2014/09/09/146494)、[三層式架構的五個好處](https://www.izenda.com/5-benefits-3-tier-architecture/)
<br/>
## Install Flask
在虛擬環境安裝flask
```bash
pip install flask
```
## Testing
為了測試我們寫出來的API server每個內部方法、類別都有回傳預期的結果,可以使用[Postman](https://www.postman.com/)或[Insomnia](https://insomnia.rest/)這類的工具來測試。
### API test
Postman與Insomnia功能相差不多,Insomnia介面很美,但Postman有自動化測試script的功能。
---
# Python 進階用法
## Python Module
記錄一些用過的Python模組...
### Collections - Counter
用法:
```python=
from collections import Counter
str = ['a', 'a', 'b']
s = Counter(str)
print(s)
# Counter({'a':2, 'b':1})
```
此模組為一個 `dict` 的子類別,提供list中元素出現的次數,將其儲存為 `dict` 。
<!-- # Python Syntax
## 判斷是否為`None` -->
## Python Decorator
裝飾器為Python中蠻重要也很方便的一種進階用法,當開發的專案越大型,會用decorator會讓你事半功倍啊👍
##### [參考資料]:[Python進階技巧 (3) — 神奇又美好的 Decorator ,嗷嗚!](https://medium.com/citycoddee/python%E9%80%B2%E9%9A%8E%E6%8A%80%E5%B7%A7-3-%E7%A5%9E%E5%A5%87%E5%8F%88%E7%BE%8E%E5%A5%BD%E7%9A%84-decorator-%E5%97%B7%E5%97%9A-6559edc87bc0)
<br/>
### 什麼是裝飾器
裝飾器也算是一種function,由於在Python中,function的地位屬於"First-class function",可以將function當作參數傳遞並執行。
裝飾器的作用就是將相同或類似的邏輯抽出來簡化,這類的funciton就稱之為裝飾器
範例如下:
```python=
def print_func_name(func):
def wrap():
print("Now use function '{}'".format(func.__name__))
func()
return wrap
def dog_bark():
print("Bark !!!")
def cat_miaow():
print("Miaow ~~~")
if __name__ == "__main__":
print_func_name(dog_bark)()
# > Now use function 'dog_bark'
# > Bark !!!
print_func_name(cat_miaow)()
# > Now use function 'cat_miaow'
# > Miaow ~~~
```
### Decorator 的 Syntax Candy
syntax candy(語法糖)就是讓語法簡化的語法,原本要寫數十行的code,若該語言有提供對應的語法糖,可能只需要寫個幾行或是幾個符號上去,就可以輕鬆完成。
Python 的decorator的語法糖就是`@`
因此,若將上述的例子改用語法糖的方式寫,會變成下面的樣子:
```python=
def print_func_name(func):
def warp():
print("Now use function '{}'".format(func.__name__))
func()
return warp
@print_func_name
def dog_bark():
print("Bark!!")
@print_func_name
def cat_miaow():
print("Miaow~~")
if __name__ = "__main__":
dog_bark()
cat_miaow()
```
### Decorator 的有序性
decorator多層的話會採"recursive"的方式處理,若一個function有兩個以上的decorators,會先合併「最內層」的decoartor吐出新的function result,再由第二個內層的decorator吃進去,最後,再由外而內將 output印出來。
> 裝飾器包裝的順序:內 --> 外,包裝完後印出output的順序: 外 --> 內
### Decorator 如何帶參數
讓decorator傳入參數,改成 `@print_func_name(parem=parem_variable)` 形式即可。
而原本的decorator的function要在原本的function外再包一層function用來解析decorator傳入的參數。
```python=
def print_func_name(time):
def decorator(func):
def warp():
print("Now use function '{}'".format(func.__name__))
print("Now Unix time is {}.".format(int(time)))
func()
return warp
return decorator
@print_func_name(time=(time.time()))
def dog_bark():
print("Bark!!")
```
### Decorator 也可以是class
Decorator除了有function decorator,也有class decorator,畢竟在Python中,function 和 class 都屬於 objects。