一、引言:从杂乱的数据到规整的结构
在生活中,我们习惯于用"表格"来组织信息------入学登记表、员工信息表、商品清单......表格让杂乱的数据变得清晰有序。
在程序中,我们同样需要这种"规整"的数据组织方式。试想一下:如果要记录一名学生的姓名、年龄、班级、成绩等信息,用变量当然可以,但如果有100个学生,难道要定义100组变量吗?用列表或字典也可以,但数据结构松散,容易出错。
面向对象编程(Object-Oriented Programming, OOP) 正是为了解决这个问题而生的。它让我们可以在程序中设计"表格"(类) ,然后生产"表格"(对象) ,最后填写数据(属性赋值) ,完美模拟现实世界的数据组织方式。
本篇博客将带领大家从零开始,全面掌握Python面向对象编程的核心知识,包括类与对象、封装、继承、多态、类型注解等。
二、初识对象
2.1 现实世界 vs 程序世界
在现实世界中,生产一件产品通常遵循这样的流程:
-
设计图纸:画出产品的结构和功能
-
生产实体:根据图纸制造出实际产品
-
投入使用:产品完成实际工作
在程序世界中,面向对象编程遵循完全相同的逻辑:
-
设计类(Class) :定义数据的结构和行为
-
创建对象(Object) :根据类实例化出具体对象
-
使用对象:调用对象的方法完成功能
2.2 类的定义与对象的创建
类(Class) 是"图纸",定义了属性和行为。对象(Object) 是"实体",是基于类创建的具体实例。
python
# 定义一个学生类(设计图纸)
class Student:
# 成员变量(属性)
name = None
age = None
# 成员方法(行为)
def say_hi(self):
print(f"大家好,我叫{self.name}!")
# 创建对象(生产实体)
stu = Student()
stu.name = "张三" # 填写属性
stu.say_hi() # 调用方法
三、成员变量与成员方法
3.1 成员变量(属性)
在类中定义的变量称为成员变量,用于存储对象的数据。每个对象都有自己的成员变量副本,互不干扰。
3.2 成员方法(行为)
在类中定义的函数称为成员方法 ,用于描述对象的行为。成员方法与普通函数的区别在于:第一个参数必须是self。
3.3 self关键字
self是成员方法定义时必须填写的参数,它代表对象自身。
python
class Student:
name = None
def say_hi(self):
# self.name 访问当前对象的name属性
print(f"大家好,我叫{self.name}!")
def say_hi2(self, msg):
print(f"大家好,我叫{self.name},{msg}")
# 使用
stu = Student()
stu.name = "里斯"
stu.say_hi() # self自动传入,无需手动传参
stu.say_hi2("欢迎学习Python!")
四、构造方法(init)
4.1 什么是构造方法?
__init__是Python类中的构造方法 ,在创建对象时会自动执行 ,主要用于初始化对象的属性。
python
class Student:
# 构造方法
def __init__(self, name, age):
self.name = name
self.age = age
print(f"学生{name}创建成功!")
# 创建对象时自动调用__init__
stu = Student("张三", 18) # 输出:学生张三创建成功!
构造方法的特点:
-
方法名必须是
__init__(前后各两个下划线) -
第一个参数必须是
self -
创建对象时自动调用
-
传入的参数会自动传递给
__init__
4.2 构造方法的优势
使用构造方法后,我们可以在创建对象的同时完成属性赋值,代码更简洁。
python
# 不使用构造方法(传统方式)
stu = Student()
stu.name = "张三"
stu.age = 18
# 使用构造方法(推荐)
stu = Student("张三", 18) # 一步到位
五、内置魔法方法
Python类中有一些以双下划线开头和结尾的特殊方法,被称为魔法方法。它们会在特定情况下自动被调用。
5.1 str:字符串方法
控制对象被打印时的显示格式。
python
class Student:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return f"【学生姓名:{self.name},年龄:{self.age}】"
stu = Student("张三", 18)
print(stu) # 【学生姓名:张三,年龄:18】
5.2 lt:小于比较
控制对象使用<运算符时的行为。
python
class Student:
def __init__(self, name, age):
self.name = name
self.age = age
def __lt__(self, other):
return self.age < other.age
stu1 = Student("张三", 18)
stu2 = Student("王五", 22)
print(stu1 < stu2) # True(按年龄比较)
5.3 eq:等于比较
控制对象使用==运算符时的行为。
python
def __eq__(self, other):
return self.age == other.age
stu1 = Student("张三", 18)
stu2 = Student("李四", 18)
print(stu1 == stu2) # True(年龄相同)
六、封装
6.1 什么是封装?
封装是面向对象三大特性之一(封装、继承、多态)。它指的是将属性和行为封装在类中,并对用户隐藏某些内部细节。
现实世界中的事物,并不是所有属性和行为都是公开的。比如手机的内部电路、操作系统的内核代码,用户不需要知道,也不应该随意访问。
6.2 私有成员
在Python中,使用双下划线开头 的变量或方法,表示私有成员,只能在类内部使用,外部无法访问。
python
class Phone:
# 私有成员变量
__brand = "华为"
__voltage = 4.0
# 私有成员方法
def __check_voltage(self):
if self.__voltage < 5:
print("电压不足")
# 公开方法
def call(self):
self.__check_voltage() # 内部可以调用私有方法
print("正在通话中")
phone = Phone()
# print(phone.__brand) # 错误!私有成员外部不可访问
# phone.__check_voltage() # 错误!
phone.call() # 正确!公开方法可以访问私有成员
封装的意义:保护数据安全,隐藏实现细节,提供清晰的外部接口。
七、继承
7.1 单继承
继承允许子类获得父类的所有成员变量和方法(私有成员除外),实现代码复用。
python
# 父类
class Phone:
producer = "华为"
def call(self):
print("正在通话中")
# 子类(单继承)
class Phone2025(Phone):
def call_6G(self):
print("正在6G通话中")
# 使用
phone = Phone2025()
print(phone.producer) # 华为(继承自父类)
phone.call() # 正在通话中(继承自父类)
phone.call_6G() # 正在6G通话中(自己的)
7.2 多继承
Python支持多继承,一个子类可以继承多个父类。
python
class NFCReader:
producer = "苹果工厂1"
def read(self):
print("正在读取数据")
class RemoteControl:
producer = "苹果工厂2"
def control(self):
print("正在遥控")
# 多继承
class MyPhone(NFCReader, RemoteControl):
pass
my_phone = MyPhone()
print(my_phone.producer) # 苹果工厂1(优先继承第一个父类)
my_phone.read() # 正在读取数据
my_phone.control() # 正在遥控
多继承的同名冲突 :当多个父类有同名成员时,按继承顺序从左到右,优先保留先继承的。
7.3 pass占位语句
pass是Python中的占位语句,用于保证代码结构的完整性,表示暂时为空,稍后补充。
python
class MyPhone(NFCReader, RemoteControl):
pass # 暂时不写任何内容,保持类定义完整
八、复写父类成员与super()
8.1 复写(Override)
如果子类对父类的成员不满意,可以在子类中重新定义同名的成员,覆盖父类的版本。
python
class Phone:
producer = "SJ"
def call(self):
print("正在通话中")
class MyPhone(Phone):
# 复写父类属性
producer = "PYT"
# 复写父类方法
def call(self):
print("正在5G通话中")
print("5G升级中!")
my_phone = MyPhone()
print(my_phone.producer) # PYT(子类的值)
my_phone.call() # 正在5G通话中(子类的方法)
8.2 调用被复写的父类成员
在子类中,如果复写了父类成员,但仍需使用父类的版本,有两种方式:
python
class MyPhone(Phone):
def call(self):
print("正在5G通话中")
# 方式1:使用父类名调用
Phone.call(self)
print(f"父类的厂商:{Phone.producer}")
# 方式2:使用super()调用
super().call()
print(f"父类的厂商:{super().producer}")
九、类型注解
9.1 为什么需要类型注解?
Python是动态类型语言,变量的类型在运行时才确定。这在带来灵活性的同时,也让代码的可读性 和IDE提示受到影响。
类型注解可以显式地说明变量、函数参数和返回值的类型,帮助IDE提供更好的代码提示,也方便其他开发者理解代码。
9.2 变量的类型注解
python
# 基本类型注解
a: int = 10
name: str = "张三"
price: float = 9.9
is_ok: bool = True
# 容器类型注解
my_list: list[int] = [1, 2, 3, 4]
my_tuple: tuple[int, str] = (1, "hello")
my_dict: dict[str, int] = {"age": 18}
# 类对象注解
class Student:
pass
stu: Student = Student()
9.3 函数(方法)的类型注解
python
# 参数类型注解 + 返回值类型注解
def add(x: int, y: int) -> int:
return x + y
result = add(1, 2) # IDE会提示result是int类型
9.4 Union类型
当变量可能是多种类型之一时,使用Union。
python
from typing import Union
# 列表中的元素可以是int或str
my_list: list[Union[int, str]] = [1, 2, "3", "4"]
# 函数参数可以是int或str,返回值也可以是int或str
def process(data: Union[int, str]) -> Union[int, str]:
return data
类型注解的特点:
-
仅仅是提示性的,不是强制性的
-
不会影响程序的运行
-
主要作用是提高代码可读性和IDE支持
十、多态与抽象类
10.1 什么是多态?
多态(Polymorphism)是指同一个行为(方法),传入不同的对象,得到不同的状态和结果。
python
class Animal:
def speak(self):
pass # 抽象方法
class Dog(Animal):
def speak(self):
print("汪汪汪")
class Cat(Animal):
def speak(self):
print("喵喵喵")
def make_noise(animal):
animal.speak() # 传入不同对象,发出不同声音
make_noise(Dog()) # 汪汪汪
make_noise(Cat()) # 喵喵喵
10.2 抽象类(接口)
抽象类是包含抽象方法的类,它定义了"有哪些方法",但不实现具体细节,由子类自行决定如何实现。
在Python中,抽象方法的方法体通常写为pass。
python
class AC:
def cool_wind(self):
"""制冷"""
pass
def hot_wind(self):
"""制热"""
pass
class GREE(AC):
def cool_wind(self):
print("格力制冷")
def hot_wind(self):
print("格力制热")
class Midea(AC):
def cool_wind(self):
print("美的制冷")
def hot_wind(self):
print("美的制热")
抽象类的设计思想:
-
父类定义标准(有哪些方法)
-
子类实现细节(如何实现)
-
面向接口编程,而非具体实现
十一、综合实战:学生信息录入系统
综合运用类、构造方法、__str__、列表等知识,完成一个学生信息录入系统。
python
class Student:
def __init__(self, name, age, address):
self.name = name
self.age = age
self.address = address
def __str__(self):
return f"【学生姓名:{self.name},年龄:{self.age},地址:{self.address}】"
def main():
students = []
total = 3
for i in range(total):
print(f"当前录入第{i+1}位学生信息,总共需录入{total}位")
name = input("请输入学生姓名:")
age = input("请输入学生年龄:")
address = input("请输入学生地址:")
stu = Student(name, age, address)
students.append(stu)
print(f"学生{i+1}信息录入完成:{stu}")
print("\n所有学生信息如下:")
for student in students:
print(student)
if __name__ == "__main__":
main()
十二、总结
本篇博客系统学习了Python面向对象编程的核心知识:
| 主题 | 核心内容 |
|---|---|
| 类与对象 | 类是图纸,对象是实体 |
| 成员方法 | 必须包含self参数 |
| 构造方法 | __init__,创建对象时自动执行 |
| 魔法方法 | __str__、__lt__、__eq__等 |
| 封装 | 私有成员以__开头,外部不可访问 |
| 继承 | 单继承、多继承,pass占位 |
| 复写 | 子类重定义父类成员,super()调用父类 |
| 类型注解 | 变量、函数参数、返回值、Union |
| 多态 | 同一行为,不同对象产生不同状态 |
| 抽象类 | 父类定义标准,子类实现细节 |
面向对象编程让我们的程序更加结构化、易维护、易扩展。它模拟了现实世界的事物组织方式,是大型项目开发中不可或缺的编程范式。