现实世界的建模工具:类与面向对象
在之前的课程中,我们处理数据的方式通常是:用一个列表装名字,用一个字典装成绩,再写几个外部函数去接收并处理它们。这种“数据是数据,函数是函数”的模式在处理规模庞大的系统时容易出现状态分散、关系混乱的问题。
**面向对象编程(Object-Oriented Programming,简称 OOP)**提供了一种更符合人类心智模型的方案:把「属于某个实体的数据(属性 Attribute)」与「能对这些数据执行的操作(方法 Method)」紧密绑定封装在一个整体中。
在 Python 官方教程第 9 章中,我们将系统学习**类(Classes)**的核心机制。
1. 概念基石:类与实例
为了彻底理解面向对象,请记住这个生动的比喻:
- 类(Class):就像一张汽车的设计图纸或烘焙饼干的模具。它定义了汽车应该有哪些零件(属性),以及汽车能够执行哪些动作(方法);
- 实例(Instance / Object):则是根据这张图纸真正生产出来的一辆具体汽车,或者用模具压烤出的一块真实饼干。
图纸只有一个,但你可以根据图纸制造出成百上千辆独立的汽车,每辆车可以有不同的车牌号和颜色。
2. 初识类定义:class 关键字
在 Python 中,使用 class 关键字来定义一个类:
class MyClass:
"""一个简单的类示例"""
i = 12345
def f(self):
return 'hello world'
类对象支持两种操作:
- 属性引用:
MyClass.i读取类变量,MyClass.f读取函数对象; - 实例化(Instantiation):通过“类名后跟圆括号”的形式,像调用函数一样创建实例对象:
此时,x = MyClass()x就是MyClass的一个具体实例。
3. 构造方法:__init__() 与实例属性
在创建实例时,我们往往需要传入初始数据为新对象定制专属的状态。这时就要用到 Python 的特殊方法 __init__()(初始化构造方法):
class ComplexNumber:
def __init__(self, realpart, imagpart):
self.r = realpart
self.i = imagpart
# 实例化并传递初始参数:
x = ComplexNumber(3.0, -4.5)
print(x.r) # 3.0
print(x.i) # -4.5
当执行 ComplexNumber(3.0, -4.5) 时,Python 会自动在底层创建一块新对象的内存,并自动调用该类中的 __init__ 方法,将新建的对象以及你传入的参数送进去。
4. 关键灵魂:为什么每个方法都有 self?
仔细观察上面的代码,你会发现无论在 __init__ 还是普通方法里,第一个参数总是 self!
self 到底是什么?
self并不是 Python 的硬性保留关键字,而是一个全社区严格遵守的约定名称;self代表的就是“当前正在调用这个方法的具体实例对象本身”!- 当你写下
x.f()时,Python 解释器在底层其实会自动将其转换为MyClass.f(x),也就是把具体的实例对象x作为第一个实参自动传给了self; - 在方法内部,所有写成
self.name = value的语句,都是在将变量牢牢绑定在当前这个专属实例身上(称为实例属性),不同实例之间绝不互相干扰。
5. 类变量 vs 实例变量
官方教程特别提醒初学者务必区分类变量与实例变量:
- 实例变量(Instance variables):用于每个实例独有的数据,通常绑定在
self.上; - 类变量(Class variables):用于类的所有实例共享的属性和方法:
class Dog:
kind = 'canine' # 类变量:所有狗都是犬科动物(所有实例共享)
def __init__(self, name):
self.name = name # 实例变量:每只狗有各自专属的名字
d = Dog('Fido')
e = Dog('Buddy')
print(d.kind) # canine
print(d.name) # Fido
print(e.name) # Buddy
致命陷阱:把可变对象(如列表)当作类变量 如果在类体中定义
tricks = []作为类变量,由于所有实例共享同一个列表,一只狗学会了打滚,所有狗的列表都会莫名其妙多出打滚!针对每个对象独有的集合,必须在__init__中使用self.tricks = []进行独立初始化。
6. 类的继承(Inheritance)
面向对象的另一大杀手锏是继承(Inheritance):当你想开发一个新类,而它与现有的某个类大部分功能相同、只是需要增加或微调某些特性时,你无需从头写起,只需让新类继承原类即可:
class DerivedClassName(BaseClassName):
"""派生类(子类)继承自基类(父类)"""
pass
子类会自动拥有父类的全部属性和方法,并且可以重写(override)父类的方法,或者通过 super() 调用父类的原有逻辑。
7. 迭代器与生成器(Generators)
在第 9 章后半部分,官方教程介绍了 Python 内部驱动循环的底层魔法:
- 迭代器(Iterator):实现了
__iter__()和__next__()方法的对象; - 生成器(Generators):使用关键字
yield编写的函数。它在每次遇到yield时暂停并交出当前值,下一次被迭代时从暂停处继续执行:
def reverse_seq(data):
for index in range(len(data)-1, -1, -1):
yield data[index]
for char in reverse_seq('golf'):
print(char, end='') # flog
生成器在处理庞大数据流或超大文本时极具威力,因为它按需逐条生成,无需一次性将整个列表载入内存。
掌握了类与面向对象,我们就拥有了构建复杂系统与专业工程的结构化思维。在接下来的两关中,我们将打开 Python 令人惊叹的宝库——标准库(Standard Library)!