一、先認識三種「變數範圍」
- 區域變數(local variable)
- 在方法(函式)內宣告,只在該方法執行期間存在。
- 不帶 self,也不帶類別名。
- 實例屬性(instance attribute)
- 以 self.xxx 存取,屬於某一個物件實例的狀態。
- 不同實例之間彼此獨立。
- 類別屬性(class attribute)
- 直接寫在 class 區塊內、方法外,或以 類別名.xxx 存取。
- 所有實例共享同一份值。
二、什麼時候要用 self?
- 需要跨方法共享的狀態 → 用 self
- 例如:檔案路徑、連線、設定、累計計數、快取、模型物件。
- 屬於這個「物件個體」的資料 → 用 self
- 每個實例不同,如每位使用者的名稱、每個紀錄器的檔案位置。
不該用 self 的時候
- 只在單一方法中短暫使用的中間變數,請用區域變數。
- 迴圈索引、臨時計算結果、一次性參數轉換等。
三、什麼時候用「類別屬性」?
- 常數或預設值,所有實例都一樣,且通常不會被單一實例特別覆寫。
- 例如:版本號、預設格式字串、全域的限制值。
- 跨實例共享的資源或統計
- 例如:建立了多少個實例的計數器。
- 注意:類別屬性被「實例屬性同名」覆寫時,該實例會看到自己的值,其他實例仍用類別值。
四、最簡單的例子
例 1:區域變數 vs 實例屬性
class Greeter:
def __init__(self, name):
self.name = name # 實例屬性:跨方法需要、每個人不同
# 下列變數若只在 __init__ 用到,就不需要 self
greeting = f"Hello, {name}" # 區域變數:只在這裡短暫使用
# print(greeting) # 用完就好,不必存成 self.greeting
def say_hi(self):
# 需要用到初始化時的 name,所以用 self.name
return f"Hi, {self.name}!"例 2:類別屬性(所有實例共享)
class Greeter:
default_prefix = "Hi" # 類別屬性:所有實例共用
count = 0 # 類別屬性:統計建立多少個實例
def __init__(self, name):
self.name = name # 實例屬性:每個人不同
Greeter.count += 1 # 用類別名操作共享計數器
def say(self):
# 使用類別屬性與實例屬性
return f"{Greeter.default_prefix}, {self.name}!"
g1 = Greeter("Alice")
g2 = Greeter("Bob")
print(Greeter.count) # 2輸出:

例 3:實務常見情境(檔案處理與 logger)
from pathlib import Path
import logging
class DocumentLogger:
# 類別屬性:共用的格式與日期格式
LOG_FORMAT = "%(asctime)s | %(levelname)s | %(name)s | %(message)s"
DATE_FORMAT = "%Y-%m-%d %H:%M:%S"
def __init__(self, output_file):
# 實例屬性:每個 logger 指向不同檔案
self.file_path = Path(output_file)
self.log_path = self.file_path.with_suffix(".log")
self.logger = self._build_logger()
def _build_logger(self):
logger = logging.getLogger(f"DocumentLogger:{self.file_path.stem}")
logger.setLevel(logging.INFO)
logger.propagate = False
for h in list(logger.handlers):
logger.removeHandler(h)
h.close()
fh = logging.FileHandler(self.log_path, encoding="utf-8")
fmt = logging.Formatter(self.LOG_FORMAT, datefmt=self.DATE_FORMAT)
fh.setFormatter(fmt)
logger.addHandler(fh)
return logger
def info(self, msg):
# 用區域變數做短暫的字串拼接就好,不必寫 self.msg
line = f"[{self.file_path.name}] {msg}"
self.logger.info(line)五、常見誤區與反例
- 誤區 1:為了「安全」把所有變數都變 self
- 壞處:物件狀態臃腫、不易讀、增加耦合,還可能誤用舊值。
- 誤區 2:應該是共享的卻放在 self
- 例如把固定常數、格式、版本放在 self,導致每個實例各自一份,難以統一管理。
- 誤區 3:應該是實例狀態卻當成類別屬性
- 例如把使用者名稱放成類別屬性,會被所有實例共享,互相覆蓋。
六、決策清單(拿不準時照這個問自己)
- 這個值會不會被其他方法使用?
- 會 → self
- 不會 → 區域變數
- 這個值對每個實例是否相同?
- 相同而且希望共享 → 類別屬性
- 不同 → 實例屬性
- 是否是一次性中間結果?
- 是 → 區域變數
- 是否需要在類別外被「所有實例」讀到同一份?
- 是 → 類別屬性(或模組常數)
七、簡短總結
- self:代表「這個物件的狀態」,用於跨方法共享且每個實例可能不同的資料。
- 類別屬性:所有實例共享的常數或統計。
- 區域變數:只在當前方法內暫用的中間值。
- 原則:最小必要狀態。需要共享才放 self;需要共享且所有實例相同才放類別屬性;其餘用區域變數。
推薦hahow線上學習python: https://igrape.net/30afN







![Python Logging 完全指南:從基礎到實戰應用; import logging ; logging.basicConfig(level=logging.INFO, handlers=[ logging.StreamHandler(), logging.FileHandler(‘app.log’, mode=’a’, encoding=’utf-8′)] ) ; inspect.currentframe().f_code.co_name #動態取得funcName Python Logging 完全指南:從基礎到實戰應用; import logging ; logging.basicConfig(level=logging.INFO, handlers=[ logging.StreamHandler(), logging.FileHandler(‘app.log’, mode=’a’, encoding=’utf-8′)] ) ; inspect.currentframe().f_code.co_name #動態取得funcName](https://i1.wp.com/savingking.com.tw/wp-content/uploads/2025/10/20251021155823_0_c16012.png?quality=90&zoom=2&ssl=1&resize=350%2C233)


近期留言