第10章 类与对象 学习目标 完成本章学习后,读者将能够:
理解Python面向对象编程的核心概念:类、实例、属性、方法 掌握魔术方法(dunder methods)的自定义与使用 运用@property实现受控属性访问与计算属性 区分实例方法、类方法与静态方法的使用场景 使用dataclass构建简洁的数据类 10.1 面向对象基础 10.1.1 类的定义与实例化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 class Person : species = "Homo sapiens" def __init__ (self, name: str , age: int ): self .name = name self .age = age def greet (self ) -> str : return f"Hello, I'm {self.name} , {self.age} years old." p = Person("Alice" , 25 ) print (p.greet()) print (p.name) print (Person.species)
学术注记 :Python中一切皆对象,类本身也是对象(type的实例)。实例通过__new__创建、__init__初始化。self不是关键字,而是约定俗成的参数名,代表实例本身,等价于其他语言中的this。
10.1.2 类属性 vs 实例属性 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Person : count = 0 def __init__ (self, name: str ): self .name = name Person.count += 1 p1 = Person("Alice" ) p2 = Person("Bob" ) print (Person.count) print (p1.name) p1.count = 100 print (Person.count) print (p1.count)
10.1.3 动态属性与__slots__ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 p = Person("Alice" ) p.email = "alice@example.com" class Point : __slots__ = ("x" , "y" ) def __init__ (self, x: float , y: float ): self .x = x self .y = y p = Point(3 , 4 )
工程实践 :__slots__可节省约40-50%内存,适用于创建大量实例的类。但会禁止动态属性添加,且影响某些继承场景。普通类无需使用。
10.2 魔术方法 10.2.1 字符串表示 1 2 3 4 5 6 7 8 9 10 11 12 13 14 class Person : def __init__ (self, name: str , age: int ): self .name = name self .age = age def __str__ (self ) -> str : return f"{self.name} ({self.age} )" def __repr__ (self ) -> str : return f"Person({self.name!r} , {self.age} )" p = Person("Alice" , 25 ) print (p) print (repr (p))
工程实践 :始终实现__repr__。当__str__未定义时,Python会回退到__repr__。__repr__应尽量返回能重建对象的合法Python表达式。
10.2.2 比较运算符 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 from functools import total_ordering@total_ordering class Student : def __init__ (self, name: str , score: float ): self .name = name self .score = score def __eq__ (self, other ) -> bool : if not isinstance (other, Student): return NotImplemented return self .score == other.score def __lt__ (self, other ) -> bool : if not isinstance (other, Student): return NotImplemented return self .score < other.score students = [Student("Bob" , 85 ), Student("Alice" , 92 ), Student("Charlie" , 78 )] print ([s.name for s in sorted (students)])
10.2.3 算术运算符 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class Vector : def __init__ (self, x: float , y: float ): self .x = x self .y = y def __add__ (self, other: "Vector" ) -> "Vector" : return Vector(self .x + other.x, self .y + other.y) def __mul__ (self, scalar: float ) -> "Vector" : return Vector(self .x * scalar, self .y * scalar) def __rmul__ (self, scalar: float ) -> "Vector" : return self .__mul__(scalar) def __abs__ (self ) -> float : return (self .x ** 2 + self .y ** 2 ) ** 0.5 def __repr__ (self ) -> str : return f"Vector({self.x} , {self.y} )" v1 = Vector(3 , 4 ) v2 = Vector(1 , 2 ) print (v1 + v2) print (v1 * 2 ) print (2 * v1) print (abs (v1))
10.2.4 容器方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class Deck : def __init__ (self ): self .cards = list (range (1 , 53 )) def __len__ (self ) -> int : return len (self .cards) def __getitem__ (self, index: int ) -> int : return self .cards[index] def __contains__ (self, card: int ) -> bool : return card in self .cards def __iter__ (self ): return iter (self .cards) deck = Deck() print (len (deck)) print (deck[0 ]) print (10 in deck) for card in deck[:5 ]: print (card)
学术注记 :实现__len__和__getitem__即可使对象支持迭代、reversed()、切片等操作,这是Python协议式多态 的体现——不需要继承特定接口,只需实现所需方法。
10.2.5 可调用对象 1 2 3 4 5 6 7 8 9 10 class Multiplier : def __init__ (self, factor: float ): self .factor = factor def __call__ (self, x: float ) -> float : return x * self .factor double = Multiplier(2 ) print (double(5 )) print (callable (double))
10.3 属性装饰器 10.3.1 @property 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class Circle : def __init__ (self, radius: float ): self .radius = radius @property def radius (self ) -> float : return self ._radius @radius.setter def radius (self, value: float ): if value <= 0 : raise ValueError("半径必须为正数" ) self ._radius = value @property def area (self ) -> float : import math return math.pi * self ._radius ** 2 c = Circle(5 ) print (c.radius) print (c.area) c.radius = 10
工程实践 :使用@property而非公开属性+getter/setter方法。property让API保持属性访问的简洁性,同时保留验证逻辑的能力。但不要过度使用——简单数据直接用公开属性即可。
10.3.2 只读属性与延迟计算 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class BankAccount : def __init__ (self, initial_balance: float = 0 ): self ._balance = initial_balance self ._transactions: list [float ] = [] @property def balance (self ) -> float : return self ._balance def deposit (self, amount: float ) -> None : if amount <= 0 : raise ValueError("存款金额必须为正数" ) self ._balance += amount self ._transactions.append(amount) def withdraw (self, amount: float ) -> bool : if 0 < amount <= self ._balance: self ._balance -= amount self ._transactions.append(-amount) return True return False
10.4 类方法与静态方法 10.4.1 三种方法对比 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class Date : def __init__ (self, year: int , month: int , day: int ): self .year = year self .month = month self .day = day def display (self ) -> str : return f"{self.year} -{self.month:02d} -{self.day:02d} " @classmethod def from_string (cls, date_str: str ) -> "Date" : y, m, d = map (int , date_str.split("-" )) return cls(y, m, d) @staticmethod def is_leap_year (year: int ) -> bool : return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0 ) d = Date.from_string("2026-04-18" ) print (d.display()) print (Date.is_leap_year(2024 ))
方法类型 第一个参数 访问类/实例 使用场景 实例方法 self实例+类 操作实例数据 类方法 cls类 工厂方法、替代构造器 静态方法 无 无 与类相关但不需类/实例数据的工具函数
工程实践 :优先使用@classmethod作为工厂方法(替代构造器),而非@staticmethod。classmethod在继承时能正确返回子类实例。
10.5 数据类 10.5.1 @dataclass基础 1 2 3 4 5 6 7 8 9 10 11 12 from dataclasses import dataclass, field@dataclass class Person : name: str age: int city: str = "北京" p1 = Person("Alice" , 25 ) p2 = Person("Alice" , 25 ) print (p1) print (p1 == p2)
10.5.2 高级选项 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 from dataclasses import dataclass, field@dataclass(frozen=True ) class Color : red: int green: int blue: int c = Color(255 , 128 , 0 ) @dataclass(order=True ) class Student : score: int name: str = field(compare=False ) students = [Student(85 , "Alice" ), Student(92 , "Bob" ), Student(78 , "Charlie" )] print ([s.name for s in sorted (students)]) @dataclass class Employee : name: str department: str salary: float = 0.0 skills: list [str ] = field(default_factory=list )
工程实践 :dataclass自动生成__init__、__repr__、__eq__等方法,大幅减少样板代码。对于简单数据容器,优先使用dataclass而非手写类。
10.6 对象模型深入 10.6.1 Python对象模型 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 def explore_object_model (): """探索Python对象模型""" class MyClass : class_var = "类变量" def __init__ (self ): self .instance_var = "实例变量" def method (self ): pass obj = MyClass() print ("对象类型:" ) print (f" type(obj) = {type (obj)} " ) print (f" type(MyClass) = {type (MyClass)} " ) print (f" type(type) = {type (type )} " ) print ("\n属性查找顺序 (MRO):" ) print (f" MyClass.__mro__ = {MyClass.__mro__} " ) print ("\n属性访问:" ) print (f" obj.__dict__ = {obj.__dict__} " ) print (f" MyClass.__dict__.keys() = {list (MyClass.__dict__.keys())[:5 ]} ..." ) print ("\n特殊属性:" ) print (f" obj.__class__ = {obj.__class__} " ) print (f" obj.__class__.__name__ = {obj.__class__.__name__} " ) explore_object_model()
学术注记 :Python的类型系统基于元类(metaclass) 。type是所有类型的元类,object是所有类的基类。类定义时,Python先调用元类的__new__创建类对象,再调用__init__初始化。理解元类是深入Python对象模型的关键。
10.6.2 描述符协议 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 class ValidatedAttribute : """验证描述符""" def __init__ (self, name: str , validator: callable ): self .name = name self .validator = validator self .private_name = f"_{name} " def __get__ (self, obj, objtype=None ): if obj is None : return self return getattr (obj, self .private_name, None ) def __set__ (self, obj, value ): if not self .validator(value): raise ValueError(f"Invalid value for {self.name} : {value} " ) setattr (obj, self .private_name, value) def __delete__ (self, obj ): raise AttributeError(f"Cannot delete {self.name} " ) class Person : age = ValidatedAttribute("age" , lambda x: isinstance (x, int ) and x >= 0 ) email = ValidatedAttribute("email" , lambda x: "@" in str (x)) def __init__ (self, age: int , email: str ): self .age = age self .email = email p = Person(25 , "alice@example.com" ) print (p.age)
学术注记 :描述符是Python属性访问的底层机制。@property本质上是描述符的语法糖。描述符协议包括__get__、__set__、__delete__方法,定义了属性访问、赋值、删除的行为。
10.6.3 属性查找机制 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 def demonstrate_attribute_lookup (): """演示属性查找顺序""" class Base : base_attr = "Base类属性" def base_method (self ): return "Base方法" class Derived (Base ): derived_attr = "Derived类属性" def derived_method (self ): return "Derived方法" obj = Derived() print ("属性查找顺序 (MRO):" ) for cls in Derived.__mro__: print (f" {cls} " ) print ("\n属性访问:" ) print (f" obj.base_attr = {obj.base_attr} " ) print (f" obj.derived_attr = {obj.derived_attr} " ) print ("\n__dict__内容:" ) print (f" obj.__dict__ = {obj.__dict__} " ) print (f" Derived.__dict__.keys() = {[k for k in Derived.__dict__.keys() if not k.startswith('_' )]} " ) demonstrate_attribute_lookup()
10.6.4 对象内存布局 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 import sysdef analyze_object_memory (): """分析对象内存布局""" class Simple : pass class WithSlots : __slots__ = ('x' , 'y' ) def __init__ (self, x, y ): self .x = x self .y = y class Normal : def __init__ (self, x, y ): self .x = x self .y = y simple = Simple() with_slots = WithSlots(1 , 2 ) normal = Normal(1 , 2 ) print ("内存占用对比:" ) print (f" 空对象: {sys.getsizeof(simple)} 字节" ) print (f" 普通类对象: {sys.getsizeof(normal)} 字节" ) print (f" __slots__对象: {sys.getsizeof(with_slots)} 字节" ) print ("\n__dict__开销:" ) print (f" 普通类__dict__: {sys.getsizeof(normal.__dict__)} 字节" ) print (f" __slots__类: 无__dict__" ) analyze_object_memory()
10.7 前沿技术动态 10.7.1 dataclass增强(PEP 681, PEP 727) 1 2 3 4 5 6 7 8 9 from dataclasses import dataclass, field@dataclass(slots=True , kw_only=True ) class Person : name: str age: int email: str = field(default="" ) p = Person(name="Alice" , age=30 )
10.7.2 类型系统与OOP 1 2 3 4 5 6 7 8 9 10 from typing import Self, TypeGuardclass Node : def __init__ (self, value: int ): self .value = value self .left: Self | None = None self .right: Self | None = None def is_leaf (self ) -> TypeGuard[Self]: return self .left is None and self .right is None
10.7.3 结构化模式匹配与类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 from dataclasses import dataclass@dataclass class Point : x: float y: float def describe (point: Point ) -> str : match point: case Point(x=0 , y=0 ): return "Origin" case Point(x=0 ): return "On Y-axis" case Point(y=0 ): return "On X-axis" case _: return "General point"
10.7.4 性能优化技术 1 2 3 4 5 6 7 8 9 from dataclasses import dataclass@dataclass(slots=True , frozen=True ) class ImmutablePoint : x: float y: float def __hash__ (self ) -> int : return hash ((self .x, self .y))
10.8 本章小结 本章系统介绍了Python类与对象的核心体系:
类与实例 :类属性共享、实例属性独有、__slots__限制魔术方法 :__str__/__repr__、比较运算符、算术运算符、容器协议、__call__属性装饰器 :@property实现受控访问与计算属性方法类型 :实例方法操作数据、类方法作工厂、静态方法作工具数据类 :@dataclass减少样板代码,支持frozen、order等选项对象模型 :元类、描述符协议、属性查找机制、内存布局10.8.1 OOP设计原则 原则 说明 Python实现 封装 隐藏内部实现细节 _private约定、@property抽象 定义接口规范 ABC抽象基类、Protocol 组合优于继承 灵活组装功能 has-a关系、依赖注入 单一职责 类只做一件事 小类、高内聚 开闭原则 对扩展开放、对修改关闭 继承、多态、装饰器
10.8.2 常见陷阱与规避 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 class Bad : items = [] a = Bad() a.items.append(1 ) b = Bad() print (b.items) class Good : def __init__ (self ): self .items = [] class Bad : @property def name (self ): return self .name class Good : @property def name (self ): return self ._name class Bad : def __eq__ (self, other ): return False class Good : def __eq__ (self, other ): if not isinstance (other, Good): return NotImplemented return self .value == other.value
10.9 练习题 基础题 创建Rectangle类,包含宽高属性及面积、周长方法,使用@property确保宽高为正数。
创建BankAccount类,实现存款、取款、查询余额,使用只读property保护余额。
使用@property实现温度转换器,可在摄氏度和华氏度之间自动转换。
进阶题 实现Vector类,支持加法、减法、标量乘法、点积和模长。
创建自定义容器类Matrix,支持索引、切片和矩阵乘法。
使用dataclass创建Student类,支持按成绩排序和平均分计算。
项目实践 图书管理系统 :编写一个程序,要求:使用dataclass定义Book(ISBN、书名、作者、价格、库存) 使用@property实现价格验证和库存管理 实现BookCatalog类,支持添加、删除、搜索、按价格排序 实现__contains__、__len__、__iter__容器协议 使用类方法实现从CSV文件导入数据 思考题 __str__和__repr__有什么区别?为什么应该始终实现__repr__?
Python的协议式多态与Java的接口机制有何本质区别?
@property的过度使用会带来什么问题?什么情况下应直接使用公开属性?
10.10 延伸阅读 10.10.1 面向对象理论 《设计模式》 (GoF) — 23种经典设计模式《面向对象分析与设计》 (Grady Booch) — OOP方法论《敏捷软件开发》 (Robert C. Martin) — OOP原则与实践SOLID原则 — 面向对象设计的五大原则10.10.2 Python对象模型 10.10.3 高级OOP技术 10.10.4 设计模式Python实现 下一章:第11章 继承与多态