---
# System prepended metadata

title: 【Python】Python 學習筆記
tags: [Python]

---

# 【Python】Python 學習筆記

## Brief

隨手紀錄撰寫 python 時候的使用概念或語法

## Context Management 上下文管理

用於程式設計中處理資源管理的技術，例如**操作文件**、**數據庫連接**、**網路連接時**，都需要資源的分配與釋放，那麼上下文管理就會是一個很好的方法，在 Python 中可以透過 `with` 語法達成

* 程式碼可以更簡潔
* 資源管理上更加安全

以下是一個 `Timer` 例子，Context Managment 需要實現兩個方法，分別為 `__enter__` 與 `__exit__`，這兩個函數代表了進入到 `with` 時初始化與離開區塊的時候要執行的動作
```python
import contextlib
import time

class Timer(contextlib.ContextDecorator):
    def __init__(self):
        self.start = 0
        self.end = 0

    def __enter__(self):
        self.start = time.time()

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.end = time.time()
        print(self.end - self.start)

if __name__ == "__main__":
    with Timer() as t:
        time.sleep(2)
```

## Decorator 修飾器
在 Python 是一種程式設計的模式之一，它可以讓一個 `class` 或 `func` ，在不需要修改程式碼的情況下加上**額外功能**，並可以用語法糖 ( Syntax Candy ) 來讓語法化簡，始其更好的閱讀與表達其意思，例如 `@` ，而 Decorator 可以是一個 `class` 或 `func`，以下是一個在 YOLO 當中所使用的例子

```python
import contextlib

class TryExcept(contextlib.ContextDecorator):
    def __init__(self, msg="", verbose=True):
        self.msg = msg
        self.verbose = verbose

    def __enter__(self):
        pass

    def __exit__(self, exc_type, exc_val, exc_tb):
        if self.verbose and exc_val:
            print(f"msg : {self.msg}")
            print(f"exc_type : {exc_type}")
            print(f"exc_val : {exc_val}")
            print(f"exc_tb : {exc_tb}")
        return True

@TryExcept(msg="Error occurred in func")
def func():
    a = 1
    b = "b"
    b = b + 1

if __name__ == "__main__":
    func()
```

```python
def func_info(func):
    def warp(*args, **kwargs):
        # Print function name and arguments
        print(f"Function name: {func.__name__}")
        print(f"Args: {args}")

        # Get docstring
        if func.__doc__:
            print(f"Docstring: {func.__doc__}")

        # Get annotations
        if func.__annotations__:
            print(f"Annotations: {func.__annotations__}")

        # Call the original function
        result = func(*args, **kwargs)

        # Print returned value
        print(f"Returned: {result}")

    return warp
```
```python
@func_info
def multiply(a: int, b: int) -> int:
    """
    Multiply two numbers.
    """
    return a * b
```
```python
if __name__ == "__main__":
    multiply(5, 3)
```

## `.py` 檔案資訊

* `__file__` : 為當前這個 `.py` 檔案的名稱
* `__name__` : 就是模組名稱，如果是被import的，就會是模組的名字例如 `exp.py` 就會顯示 `exp`，如果是直接執行，就會是 `__main__`
* `__doc__` : 能夠得到當前檔案字串，或者一個類別的字串


## Dict 字典
Dict 是一種藉由 key-value 的資料結構，key 值是 immutable ( 不可改變的 )，而插入元素是有序的，但是鍵值排列是無序的

* 創建字典
```python!
d = dict(a=1, b=2, c=3)
a = ["alan", "alice"]
b = [32, 42, 52]
d = dict(zip(a, b))
```
* 更新字典
在 `args` 這行中，由於右側輸入 `kwargs` 優先度比較高，所以執行後本來的 `x` 會被取代掉
```python
kwargs = {"x": 5}
override = {"x": 7, "y": 10}
args = {**override, **kwargs}  # highest priority args on the right
kwargs.update(override)
```


## Enum
列舉能夠提供語意化、型別安全和集中觀禮的常數定義，可以降低程式碼的複雜度，可以讓程式碼避免以硬編碼方式、或者限制鍵值方式，提高程式碼的維護程度
```python
class SubjectType(Enum):
    TRAIN = auto()
    VAL = auto()
    TEST = auto()

    def to_string(self) -> str:
        return self.name.lower()

    @classmethod
    def from_string(cls, value: str) -> 'SubjectType':
        return cls[value.upper()]
```


## Mutable vs Immutable
* Mutable obejct 在作為參數傳入函數時，為 Call by reference，在修改時會影響物件本身，所以要仔細確保函數功能是否具備修改物件屬性的
```python
def fun(d):
    for k, v in d.items():
        d[v] = 5 # modify 
```
*  Immutable objects 在 iterate 時候會不斷創建新的物件，造成開銷
*  Multable object : `list`, `set`, `dict`
*  Immultable object : `int`, `object`, `string`, `tuple`

## Typing 類別提示
在 Python 當中，由於動態型別的特性，也就是執行期間可以更改變數型態，因此在開發過程某變數的型態可能會改變，但對於程式開發者或許不知道其變數變成了甚麼型態，因此類別提示就適合在這樣的情況下使用，用於提示開發者該變數資料型態或者函數輸入資料型態

```python
from typing import Dict, List

def fun(d:Dict[str, int], key:int):
    d[str(key)] = key
```

### Protocol 
Protocol 用於提示某個類別是否有實現特定方法，也就是你所設計類別需要實現某個方法才能夠被該函數或類別使用

```python
class DataLoader(Protocol):
    """Abstract interface: Loads data, returns Dict[str, Any] (DIP, ISP)."""
    def load(self, path: Path) -> Dict[str, Any]:
        ...
        
class CSVLoader:
    """CSV-specific loader (SRP, LSP)."""
    def load(self, path: Path) -> Dict[str, pd.DataFrame]:
        if not path.exists():
            raise FileNotFoundError(f"CSV file not found: {path}")
        df = pd.read_csv(path)
        return {"data": df}

class MatLoader:
    """MAT-specific loader (SRP, LSP, ISP - Separates non-DataFrame formats)."""
    def load(self, path: Path) -> Dict[str, Any]:
        if not path.exists():
            raise FileNotFoundError(f"MAT file not found: {path}")
        data = loadmat(path)
        return {"data": data}
```