第一章:類定義的基礎概念
1.1 類定義的起源與演變
類定義的概念源於Simula語言,但Python的實現深受Smalltalk和C++影響。Guido van Rossum在Python 0.9中引入類,作為動態語言的輕量級OOP實現。早期,Python 2區分“舊式類”(經典繼承)和“新式類”(現代繼承),後者引入了super()和描述符協議。從Python 3起,所有類均為新式類,統一了繼承模型。
為什麼類定義如此重要?Python是鴨子類型語言(“如果它走路像鴨子,叫聲像鴨子,那就是鴨子”),類定義通過協議(protocols)而非嚴格接口實現多態。這讓類更靈活:無需顯式繼承,就能通過實現相同方法“兼容”。在函數式視角下,類方法是“有狀態的函數”,實例變量是閉包般的狀態捕獲。
演變亮點包括:
- Python 3.6:dataclasses簡化了樣板代碼。
- Python 3.7:dataclasses後置字段。
- Python 3.9:結構化模式匹配擴展了類的使用。
- Python 3.10+:參數規範和更好的錯誤消息。
這些變化讓類定義從靜態聲明轉向動態工具,尤其在AI和數據工程領域。
1.2 基本語法:class關鍵字的使用
類定義以class關鍵字開頭,後跟類名(PascalCase約定)和可選基類:
class MyClass:
"""類文檔字符串:描述類的目的和用法。"""
# 類變量:所有實例共享
class_var = "共享值"
def __init__(self, arg1, arg2):
"""初始化方法,相當於構造函數。"""
self.instance_var = arg1 # 實例變量
self._protected = arg2 # 約定:保護變量(單下劃線)
def public_method(self):
"""公共方法:可從外部調用。"""
return f"實例變量: {self.instance_var}"
def _internal_method(self):
"""內部方法:約定不從外部調用。"""
return self._protected * 2
創建實例:
obj = MyClass("hello", 42)
print(obj.public_method()) # 實例變量: hello
print(MyClass.class_var) # 共享值
注意:Python無私有成員,一切基於約定(_private、__name mangling)。__init__是特殊方法(dunder),不是真正的構造函數——實例在__new__中創建,__init__僅初始化。
類體在定義時執行,適合定義類變量或執行一次性初始化:
class DynamicClass:
counter = 0 # 類變量
def __init__(self):
DynamicClass.counter += 1 # 靜態計數
1.3 類與實例:命名空間與屬性解析
類是一個命名空間,存儲方法和類變量;實例有自己的__dict__字典。屬性查找順序(MRO,Method Resolution Order)從實例到類,再到基類。
示例:屬性動態添加
class FlexibleClass:
def __init__(self):
self.dynamic_attr = "動態添加"
obj = FlexibleClass()
obj.new_method = lambda: "新方法" # 動態綁定
print(obj.new_method()) # 新方法
這體現了Python的動態性:類定義後仍可修改。但在生產中,避免過度動態化,以維護可讀性。
類變量 vs 實例變量:
class Counter:
count = 0 # 類變量
def __init__(self):
self.local_count = Counter.count # 複製到實例
Counter.count += 1
c1 = Counter()
c2 = Counter()
print(c1.local_count, c2.local_count) # 0 1
print(Counter.count) # 2
修改類變量影響所有實例,除非實例有同名屬性。
1.4 類作為函數:可調用類
類本身是可調用的,返回新實例:
class CallableClass:
def __init__(self, value):
self.value = value
def __call__(self, *args, **kwargs):
"""使實例可調用。"""
return self.value + sum(args)
cls = CallableClass(10)
instance = cls(5) # __init__調用,等價於CallableClass(5)
result = instance(1, 2) # __call__調用,13
這讓類像高階函數:接受參數創建“函數工廠”。
第二章:類方法與函數的互動
2.1 實例方法:綁定到self
實例方法是類定義的核心函數形式。第一參數隱式為self:
class BankAccount:
def __init__(self, balance=0):
self.balance = balance
def deposit(self, amount):
"""存款方法。"""
if amount > 0:
self.balance += amount
return self.balance
def withdraw(self, amount):
"""取款方法。"""
if 0 < amount <= self.balance:
self.balance -= amount
return self.balance
account = BankAccount(100)
print(account.deposit(50)) # 150
self是實例引用,確保方法訪問正確狀態。脱綁(unbound)方法:account.deposit 是函數對象,可手動調用account.deposit(account, 50)。
2.2 類方法:@classmethod裝飾器
類方法綁定到類本身,用cls參數:
class BankAccount:
rate = 0.05 # 類變量:利率
@classmethod
def from_string(cls, account_str):
"""類方法:從字符串創建實例。"""
name, balance = account_str.split(':')
return cls(name, float(balance)) # cls替代BankAccount
def __init__(self, name, balance):
self.name = name
self.balance = balance
# 使用
account = BankAccount.from_string("Alice:1000")
print(account.balance) # 1000.0
優勢:子類調用時,使用子類創建實例,支持工廠模式。
2.3 靜態方法:@staticmethod裝飾器
靜態方法如普通函數,掛載在類上,無綁定:
class MathUtils:
@staticmethod
def add(x, y):
"""靜態方法:純函數,無需實例。"""
return x + y
@staticmethod
def is_prime(n):
if n < 2:
return False
for i in range(2, int(n**0.5) + 1):
if n % i == 0:
return False
return True
print(MathUtils.add(3, 4)) # 7
print(MathUtils.is_prime(17)) # True
適合工具函數,避免污染命名空間。
2.4 方法解析順序:MRO與super()
多繼承時,MRO決定調用順序。用C3線性化算法計算:
class A:
def method(self):
print("A")
class B(A):
pass
class C(A):
def method(self):
print("C")
super().method() # 調用A
class D(B, C): # MRO: D -> B -> C -> A
def method(self):
print("D")
super().method() # B無method,跳C
d = D()
d.method()
# 輸出: D
# C
# A
super()動態分派,確保正確基類調用。在單繼承中,也推薦super()以便未來擴展。
第三章:特殊方法:讓類像內置類型
3.1 初始化與銷燬:new__與__del
__new__是靜態方法,控制實例創建:
class Singleton:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
def __init__(self):
self.value = "Singleton value"
s1 = Singleton()
s2 = Singleton()
print(s1 is s2) # True
__del__是析構器,在垃圾回收時調用:
class Resource:
def __init__(self, name):
self.name = name
print(f"創建 {name}")
def __del__(self):
print(f"銷燬 {self.name}")
r = Resource("文件句柄")
del r # 觸發__del__
注意:__del__不保證立即調用,受循環引用影響。用contextmanager替代資源管理。
3.2 表示方法:str、repr__與__format
自定義字符串表示:
class Point:
def __init__(self, x, y):
self.x, self.y = x, y
def __str__(self):
return f"Point({self.x}, {self.y})" # 用户友好
def __repr__(self):
return f"Point({self.x!r}, {self.y!r})" # 調試用
def __format__(self, spec):
if spec == 'polar':
import math
r = math.sqrt(self.x**2 + self.y**2)
theta = math.atan2(self.y, self.x)
return f"r={r:.2f}, theta={theta:.2f}"
return str(self)
p = Point(3, 4)
print(str(p)) # Point(3, 4)
print(repr(p)) # Point(3, 4)
print(f"{p:polar}") # r=5.00, theta=0.93
__repr__應可重現對象,__str__優先用户輸出。
3.3 比較方法:eq、__lt__等
實現可哈希和可排序:
class Point:
def __init__(self, x, y):
self.x, self.y = x, y
def __eq__(self, other):
if isinstance(other, Point):
return self.x == other.x and self.y == other.y
return NotImplemented
def __lt__(self, other):
if isinstance(other, Point):
return (self.x, self.y) < (other.x, other.y)
return NotImplemented
def __hash__(self):
return hash((self.x, self.y))
p1 = Point(1, 2)
p2 = Point(1, 2)
print(p1 == p2) # True
print(hash(p1) == hash(p2)) # True
從Python 3.7起,dataclasses自動生成這些。
3.4 容器協議:len、__getitem__等
讓類像列表:
class Vector:
def __init__(self, data):
self.data = list(data)
def __len__(self):
return len(self.data)
def __getitem__(self, index):
return self.data[index]
def __setitem__(self, index, value):
self.data[index] = value
def __delitem__(self, index):
del self.data[index]
def __iter__(self):
return iter(self.data)
v = Vector([1, 2, 3])
print(len(v)) # 3
print(v[0]) # 1
v[1] = 4
for x in v: # 迭代
print(x) # 1 4 3
del v[2]
這啓用for循環、切片等,支持itertools集成。
3.5 上下文管理:enter__與__exit
實現with語句:
class FileManager:
def __init__(self, filename, mode="r"):
self.filename = filename
self.mode = mode
self.file = None
def __enter__(self):
self.file = open(self.filename, self.mode)
return self.file
def __exit__(self, exc_type, exc_val, exc_tb):
if self.file:
self.file.close()
if exc_type:
print(f"異常: {exc_val}")
return False # 不抑制異常
with FileManager("example.txt", "w") as f:
f.write("Hello, context!")
# 自動關閉
優於try-finally,異常安全。
第四章:繼承與多態
4.1 單繼承:擴展基類
繼承複用代碼:
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
return f"{self.name} 發出聲音"
class Dog(Animal):
def speak(self):
return f"{self.name} 汪汪!" # 多態覆蓋
d = Dog("Buddy")
print(d.speak()) # Buddy 汪汪!
super()在__init__中調用基類:
class Dog(Animal):
def __init__(self, name, breed):
super().__init__(name)
self.breed = breed
4.2 多繼承:菱形問題與MRO
多繼承支持複雜層次:
class Flyer:
def fly(self):
return "飛行中"
class Swimmer:
def swim(self):
return "游泳中"
class Duck(Flyer, Swimmer):
def __init__(self):
pass
duck = Duck()
print(duck.fly()) # 飛行中
print(duck.swim()) # 游泳中
菱形繼承:A -> B,C -> D,使用MRO解決:
class A:
def method(self):
print("A")
class B(A):
pass
class C(A):
def method(self):
print("C")
super().method()
class D(B, C):
pass
print(D.mro()) # [<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
C3確保每個基類出現一次,避免重複調用。
4.3 混合繼承:接口模擬與協議
Python無顯式接口,用ABC(Abstract Base Classes)強制實現:
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self):
pass
class Rectangle(Shape):
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
return self.width * self.height
rect = Rectangle(3, 4)
print(rect.area()) # 12
# Shape() # TypeError: Can't instantiate abstract class
協議(如Iterable)通過鴨子類型實現,無需繼承。
4.4 委託與組合:優於繼承
組合優先:將對象作為屬性委託:
class Engine:
def start(self):
return "引擎啓動"
class Car:
def __init__(self):
self.engine = Engine()
def start(self):
return self.engine.start() + ",汽車前進"
car = Car()
print(car.start()) # 引擎啓動,汽車前進
減少耦合,避免“脆弱基類問題”。
第五章:屬性與描述符
5.1 屬性:@property裝飾器
動態計算屬性:
class Circle:
def __init__(self, radius):
self._radius = radius
@property
def radius(self):
return self._radius
@radius.setter
def radius(self, value):
if value < 0:
raise ValueError("半徑不能負")
self._radius = value
@property
def area(self):
import math
return math.pi * self._radius ** 2
c = Circle(5)
print(c.area) # ~78.54
c.radius = 10 # setter觸發
print(c.area) # ~314.16
# c.radius = -1 # ValueError
getter/setter/deleter使屬性像字段,卻有邏輯。
5.2 描述符協議:自定義屬性行為
描述符是類,實現__get__、__set__等:
class Validated:
def __init__(self, min_val=0):
self.min_val = min_val
def __set_name__(self, owner, name): # Python 3.6+
self.name = name
def __get__(self, obj, objtype=None):
if obj is None:
return self
return obj.__dict__[self.name]
def __set__(self, obj, value):
if value < self.min_val:
raise ValueError(f"{self.name} 不能小於 {self.min_val}")
obj.__dict__[self.name] = value
class Product:
price = Validated(0.01)
def __init__(self, price):
self.price = price
p = Product(10.0)
print(p.price) # 10.0
# p.price = 0 # ValueError
描述符用於框架如SQLAlchemy的ORM。
5.3 slots:內存優化
限制實例屬性,節省內存:
class Point:
__slots__ = ('x', 'y')
def __init__(self, x, y):
self.x = x
self.y = y
p = Point(1, 2)
# p.z = 3 # AttributeError
import sys
print(sys.getsizeof(p)) # ~48 bytes vs 56+ for dict
適合大量實例,如遊戲實體。
5.4 類屬性:ClassVar與Final
從typing導入:
from typing import ClassVar, Final
class Config:
API_URL: Final[str] = "https://api.example.com" # 不可變
debug: ClassVar[bool] = True # 不存儲在實例
def __init__(self):
self.user_id = 123
Final防止重賦,ClassVar提示類型檢查器。
第六章:高級主題:元類與動態類
6.1 元類基礎:type是所有類的元類
元類是“類的類”,控制類創建:
class Meta(type):
def __new__(cls, name, bases, attrs):
# 修改attrs
attrs['created_at'] = '動態添加'
return super().__new__(cls, name, bases, attrs)
def __init__(cls, name, bases, attrs):
print(f"創建類 {name}")
class MyClass(metaclass=Meta):
pass
print(MyClass.created_at) # 動態添加
# 輸出: 創建類 MyClass
type是默認元類:type('ClassName', (Base,), {'attr': value})
6.2 自定義元類:驗證與日誌
class ValidatedMeta(type):
def __new__(cls, name, bases, attrs):
# 強制抽象方法
if name != 'Base':
if not any('method' in k for k in attrs if not k.startswith('_')):
raise TypeError(f"{name} 缺少方法")
return super().__new__(cls, name, bases, attrs)
class Base(metaclass=ValidatedMeta):
pass
class Good(Base):
def method(self):
pass
# class Bad(Base): # TypeError
# pass
用於框架,確保接口一致。
6.3 ABCMeta:抽象基類元類
abc.ABCMeta擴展type,支持@abstractmethod。
6.4 動態類創建:type()與exec
運行時代碼:
def create_class(name, fields):
attrs = {'__init__': lambda self: setattr(self, fields[0], 0)}
return type(name, (), attrs)
Dynamic = create_class('Dynamic', ['value'])
d = Dynamic()
d.value = 42 # 通過動態添加
小心安全:在沙箱中使用。
第七章:dataclasses與現代簡化
7.1 @dataclass:自動生成樣板
Python 3.7+標準庫:
from dataclasses import dataclass, field
from typing import List
@dataclass
class Employee:
name: str
id: int = field(default=0)
skills: List[str] = field(default_factory=list)
def promote(self):
self.id += 1
e = Employee("Alice", skills=["Python"])
print(e) # Employee(name='Alice', id=0, skills=['Python']) # 自動__repr__
print(e == Employee("Alice")) # True,自動__eq__
生成__init__、repr、__eq__等。post_init自定義初始化。
7.2 高級dataclasses:frozen與order
@dataclass(frozen=True) # 不可變
class Point:
x: float
y: float
@dataclass(order=True) # 支持< > 等
class Version:
major: int
minor: int
v1 = Version(1, 2)
v2 = Version(1, 3)
print(v1 < v2) # True
frozen用hash()安全。
7.3 asdict與astuple:序列化
from dataclasses import asdict, astuple
e_dict = asdict(e) # {'name': 'Alice', 'id': 0, 'skills': ['Python']}
e_tuple = astuple(e) # ('Alice', 0, ['Python'])
集成JSON或數據庫。
7.4 自定義dataclass行為
繼承dataclass:
@dataclass
class ValidatedEmployee(Employee):
def __post_init__(self):
if not self.name:
raise ValueError("名字不能為空")