📄 本篇摘要
类的解剖图:字段(field)存数据(状态),方法(method)做操作(行为),属性(property)是【看起来像字段的方法】——读取时可以做校验、计算、缓存。三者分工清楚,类的接口才干净。
一、字段:对象的状态
字段=挂在对象身上的变量。分两种(5-15 见过):
- 实例字段:每个对象一份(
self.name); - 类字段(静态字段):全体共享一份(
Dog.species),常用于常量、计数器。
字段是对象【现在是什么样】的记录——对象的状态。状态越少越好管:能算出来的别存(存【出生日期】而不是【年龄】——年龄会过期,日期不会)。
二、方法:对象的行为
方法=定义在类里的函数,第一个参数固定接住实例自己(Python 叫 self)。特殊成员:
- 构造方法
__init__(5-25 专题); - 魔术方法:
__str__(打印成什么)、__len__、__eq__……让对象融入语言的操作体系; - 类方法/静态方法:不依赖实例的方法(
@classmethod接类、@staticmethod什么都不接)。
三、属性:伪装成字段的方法
直接暴露字段有个大问题:acct.balance = -999 谁也拦不住。属性(property)的解法:语法上照字段用(acct.balance),实际走方法——读时校验、写时把关、值可现算。
class Account:
def __init__(self):
self._balance = 0 # 带下划线:内部字段,请勿直接碰
@property
def balance(self): # 读 balance 实际调它
return self._balance
@balance.setter
def balance(self, value):
if value < 0:
raise ValueError("余额不能为负")
self._balance = value
好处:调用方永远写 acct.balance——今天直接返回字段,明天改成实时查数据库,调用代码一行不用改。属性是 Python 对【统一访问原则】的实现。
四、动手 Demo:三种成员同台
class Circle:
count = 0 # 类字段:统计造了多少个圆
def __init__(self, r):
if r <= 0:
raise ValueError("半径必须为正")
self._r = r # 实例字段(内部)
Circle.count += 1
@property
def area(self): # 属性:现算,不存
return 3.14159 * self._r ** 2
@property
def r(self):
return self._r
@r.setter
def r(self, value): # 写时把关
if value <= 0:
raise ValueError("半径必须为正")
self._r = value
@classmethod
def total(cls): # 类方法:不依赖实例
return cls.count
def __str__(self): # 魔术方法:打印长相
return f"圆(r={self._r}, 面积={self.area:.2f})"
c = Circle(2)
print(c) # 触发 __str__ -> 属性 area 现算
c.r = 5 # 走 setter 校验后合法修改
print(c)
print("已造圆数:", Circle.total())
try:
c.r = -1
except ValueError as e:
print("拦下非法赋值:", e)
沙箱实跑输出:
圆(r=2, 面积=12.57)
圆(r=5, 面积=78.54)
已造圆数: 1
拦下非法赋值: 半径必须为正
看到什么算成功:打印自动走 __str__;面积随半径现算更新;负半径被 setter 拦下。
读结果:面积没有存进任何字段——它由半径现算(状态最少化);外部代码看起来只是在读 c.area,背后是方法在跑。字段、方法、属性三者的边界,就是【数据怎么存】与【数据怎么给】的分界。
五、30 秒实验
给 Circle 加一个 perimeter 属性(2 * 3.14159 * self._r)重跑——c.perimeter 立刻可用。属性加零成本:调用方语法完全不变。
六、常见误区
| 误区 | 真相 |
|---|---|
| 字段全公开最方便 | 公开字段=放弃把关权;后期想加校验就得改所有调用方 |
| 能存的都存起来 | 状态越少 bug 越少;能从现有字段算出的就现算(属性正合适) |
| property 只是语法糖 | 它是【统一访问原则】的实现——内部实现可以随便换,接口永远稳定 |
自测三问(点击展开)
1. **实例字段和类字段的区别?** 答:实例字段每对象一份存独立状态;类字段全体共享(常量、计数器)。 2. **property 解决什么问题?** 答:读写字段时能插入校验/计算/缓存,而调用方语法保持像字段一样简单。 3. **为什么【存出生日期不存年龄】?** 答:年龄可由日期现算且会过期——存了就会忘记更新(状态最少化)。- ☐ 5-17 学习完成
相关笔记
- C++ 10.4 · 类成员 —— 成员的 C++ 画法(含静态成员)
- C++ 10.4.3 · 静态类成员 —— 类字段的 C++ 版
- 构造与析构 ——
__init__的专题篇 - ⌨️ 编程原理 · 索引 —— 全课路线图