欢迎光临
我们一直在努力

Python 基础语法(四):类与对象、封装/继承/多态、魔术方法

1. 引言

很多初学者在掌握 Python 基础语法后,都会卡在同一个问题上:如何从「写脚本」进阶到「写程序」?答案就是面向对象编程(OOP)。本文正是为已经掌握 Python 基础语法、希望系统学习面向对象思想的读者而写,帮你彻底搞懂类与对象、封装、继承、多态这三大特性,以及 __init__、__str__、__add__ 等魔术方法背后的运行机制。

读完本文,你将能够:独立设计并定义自己的类,用封装保护数据安全,用继承实现代码复用,用多态写出更灵活的代码,并通过魔术方法让自定义类拥有与内置类型一致的使用体验。全文配有大量可直接运行的示例和综合实战,跟着敲一遍,你就能真正把面向对象用起来。
Python 从设计之初就是一门面向对象的语言,它的很多内置类型(如 int、str、list)本质上都是类。掌握类与对象、三大特性(封装、继承、多态)以及魔术方法,是写出结构清晰、易于维护的 Python 代码的关键一步。

2. 类与对象基础

2.1 什么是类,什么是对象

类(Class) 是对象的蓝图或模板,它定义了一组属性和方法。对象(Object) 是根据类创建出来的具体实例。

举个例子:Dog 是一个类,它定义了所有狗共有的特征(名字、年龄)和行为(叫、跑);而 小白 是 Dog 类的一个具体对象。

2.2 定义类与创建对象

使用 class 关键字定义类,使用 类名() 创建对象:

class Dog:
"""狗类"""
# 类属性:所有实例共享
species = "犬科"

# 构造方法:创建对象时自动调用
def __init__(self, name, age):
self.name = name # 实例属性
self.age = age

# 实例方法
def bark(self):
print(f"{self.name} 在汪汪叫!")

def run(self):
print(f"{self.name} 跑得飞快!")

# 创建对象
dog1 = Dog("小白", 2)
dog2 = Dog("大黄", 3)

# 访问属性
print(dog1.name) # 小白
print(dog1.species) # 犬科(类属性)

# 调用方法
dog1.bark() # 小白 在汪汪叫!
dog2.run() # 大黄 跑得飞快!

2.3 self 是什么

self 代表实例本身。在类内部定义方法时,第一个参数必须是 self(名字可以改,但约定俗成用 self)。调用实例方法时,Python 会自动把对象本身传给 self,所以调用时不需要手动传这个参数。

2.4 类属性与实例属性

  • 类属性:定义在类内部、方法外部,所有实例共享。
  • 实例属性:在 __init__ 中通过 self.xxx 定义,每个实例独立拥有。

class Student:
school = "第一中学" # 类属性

def __init__(self, name):
self.name = name # 实例属性

s1 = Student("小明")
s2 = Student("小红")
print(s1.school) # 第一中学
print(s2.school) # 第一中学

3. 封装

3.1 什么是封装

封装(Encapsulation) 是指将数据(属性)和操作数据的方法绑定在一起,并对外隐藏内部实现细节,只暴露必要的接口。封装的好处是:保护数据不被随意修改,提高代码的安全性和可维护性。

3.2 私有属性与方法

在 Python 中,约定使用单下划线 _ 表示「受保护」成员,使用双下划线 __ 表示「私有」成员。双下划线开头的属性或方法,在类外部不能直接访问(Python 会做名称改写)。

class BankAccount:
def __init__(self, owner, balance):
self.owner = owner
self.__balance = balance # 私有属性

def deposit(self, amount):
"""存款"""
if amount > 0:
self.__balance += amount
print(f"存入 {amount} 元,余额 {self.__balance} 元")
else:
print("存款金额必须大于 0")

def withdraw(self, amount):
"""取款"""
if 0 < amount <= self.__balance:
self.__balance -= amount
print(f"取出 {amount} 元,余额 {self.__balance} 元")
else:
print("余额不足或金额非法")

def get_balance(self):
"""对外提供只读接口"""
return self.__balance

acc = BankAccount("张三", 1000)
acc.deposit(500) # 存入 500 元,余额 1500 元
acc.withdraw(200) # 取出 200 元,余额 1300 元
print(acc.get_balance()) # 1300

# 直接访问私有属性会报错
# print(acc.__balance) # AttributeError

3.3 使用 @property 实现优雅的封装

@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("半径必须大于 0")
self._radius = value

@property
def area(self):
"""计算面积(只读属性)"""
return 3.14159 * self._radius ** 2

c = Circle(5)
print(c.radius) # 5
print(c.area) # 78.53975
c.radius = 10 # 走 setter 校验
print(c.area) # 314.159

# 传入非法值(负数或非数字)时,捕获 ValueError 并给出友好提示
try:
c.radius = 3
except ValueError as e:
print(f"设置失败:{e}") # 设置失败:半径必须大于 0

try:
c.radius = "abc"
except ValueError as e:
print(f"设置失败:{e}") # 设置失败:半径必须大于 0

下面用一张表格对比 _ 单下划线、__ 双下划线和 @property 三种封装方式的差异:

封装方式访问权限使用场景示例代码
_ 单下划线 类外部可访问,仅作「约定」提示,不强制限制 表示「受保护」成员,供类内部及子类使用,避免误用 self._radius = radius
__ 双下划线 类外部不能直接访问(Python 会做名称改写) 表示「私有」成员,防止外部随意修改,需通过方法间接访问 self.__balance = balance
@property 通过属性语法访问,可在 getter/setter 中加校验逻辑 对外暴露「看似普通属性」的接口,同时保护数据、保持代码简洁 @property def radius(self): …

4. 继承

4.1 什么是继承

继承(Inheritance) 允许一个类(子类)获取另一个类(父类)的属性和方法,从而实现代码复用。子类可以继承父类的所有公开成员,也可以添加自己的新成员,或重写父类的方法。

4.2 基本继承语法

class Animal:
def __init__(self, name):
self.name = name

def eat(self):
print(f"{self.name} 正在吃东西")

def sleep(self):
print(f"{self.name} 正在睡觉")

class Dog(Animal): # 继承 Animal
def bark(self):
print(f"{self.name} 在汪汪叫")

class Cat(Animal): # 继承 Animal
def meow(self):
print(f"{self.name} 在喵喵叫")

dog = Dog("小白")
cat = Cat("小花")

dog.eat() # 小白 正在吃东西(继承自父类)
dog.bark() # 小白 在汪汪叫(子类自己的方法)
cat.sleep() # 小花 正在睡觉(继承自父类)
cat.meow() # 小花 在喵喵叫

4.3 方法重写与 super()

子类可以重写父类的方法。在重写时,通常用 super() 调用父类的同名方法,以复用父类逻辑:

class Animal:
def __init__(self, name):
self.name = name

def speak(self):
print(f"{self.name} 发出声音")

class Dog(Animal):
def __init__(self, name, breed):
super().__init__(name) # 调用父类构造方法
self.breed = breed

def speak(self): # 重写父类方法
print(f"{self.name}{self.breed})在汪汪叫")

dog = Dog("小白", "金毛")
dog.speak() # 小白(金毛)在汪汪叫

4.4 多继承

Python 支持多继承,即一个子类可以继承多个父类:

class Flyable:
def fly(self):
print("我会飞!")

class Swimmable:
def swim(self):
print("我会游泳!")

class Duck(Flyable, Swimmable):
pass

duck = Duck()
duck.fly() # 我会飞!
duck.swim() # 我会游泳!

5. 多态

5.1 什么是多态

多态(Polymorphism) 是指同一个方法调用,在不同对象上表现出不同的行为。多态让代码更加灵活——我们不需要关心对象的具体类型,只要它拥有我们调用的方法即可。

5.2 多态的典型实现

class Animal:
def speak(self):
pass

class Dog(Animal):
def speak(self):
print("汪汪汪")

class Cat(Animal):
def speak(self):
print("喵喵喵")

class Duck(Animal):
def speak(self):
print("嘎嘎嘎")

# 多态:同一个函数,处理不同类型的对象
def make_speak(animal):
animal.speak()

animals = [Dog(), Cat(), Duck()]
for animal in animals:
make_speak(animal)

# 输出:
# 汪汪汪
# 喵喵喵
# 嘎嘎嘎

5.3 鸭子类型

Python 的多态更多体现在「鸭子类型」上:只要一个对象拥有某个方法,就可以调用它,而不必关心它属于哪个类。

class Car:
def run(self):
print("汽车在跑")

class Bike:
def run(self):
print("自行车在骑")

def start(obj):
obj.run() # 只要对象有 run 方法即可

start(Car()) # 汽车在跑
start(Bike()) # 自行车在骑

6. 魔术方法

6.1 什么是魔术方法

魔术方法(Magic Methods) 也叫「特殊方法」或「双下划线方法」,是 Python 中以双下划线开头和结尾的方法(如 __init__、__str__)。它们由 Python 在特定场景下自动调用,用来定制类的行为。

6.2 常用魔术方法一览

魔术方法触发时机说明
__init__(self, …) 创建对象时 构造方法,初始化实例属性
__str__(self) print(obj) / str(obj) 返回人类可读的字符串
__repr__(self) 交互式环境直接输入对象 返回开发者可读的字符串
__len__(self) len(obj) 返回长度
__getitem__(self, key) obj[key] 按下标取值
__setitem__(self, key, value) obj[key] = value 按下标赋值
__iter__(self) for x in obj 返回迭代器
__eq__(self, other) obj1 == obj2 判断相等
__lt__(self, other) obj1 < obj2 判断小于
__add__(self, other) obj1 + obj2 定义加法行为
__call__(self, …) obj(…) 让对象像函数一样被调用

6.3 实战示例:自定义一个「分数」类

下面综合运用多个魔术方法,实现一个支持加减比较的分数类:

class Fraction:
def __init__(self, numerator, denominator):
if denominator == 0:
raise ValueError("分母不能为 0")
self.numerator = numerator
self.denominator = denominator

def __str__(self):
"""print 时显示为 分子/分母"""
return f"{self.numerator}/{self.denominator}"

def __repr__(self):
return f"Fraction({self.numerator}, {self.denominator})"

def __add__(self, other):
"""支持分数 + 分数"""
new_num = self.numerator * other.denominator + other.numerator * self.denominator
new_den = self.denominator * other.denominator
return Fraction(new_num, new_den)

def __eq__(self, other):
"""支持 == 比较"""
return (self.numerator * other.denominator) == (other.numerator * self.denominator)

def __lt__(self, other):
"""支持 < 比较"""
return (self.numerator * other.denominator) < (other.numerator * self.denominator)

f1 = Fraction(1, 2)
f2 = Fraction(1, 3)

print(f1) # 1/2(触发 __str__)
print(f1 + f2) # 5/6(触发 __add__)
print(f1 == Fraction(2, 4)) # True(触发 __eq__)
print(f1 < f2) # False(触发 __lt__)

接下来,我们看看如何对 Fraction 类进行异常处理。当分母为 0 时,构造方法会抛出 ValueError;当参与运算的对象不是 Fraction 类型时,会抛出 TypeError。下面用 try-except-finally 完整演示这两种异常的捕获与处理思路:

# 1. 捕获分母为 0 的 ValueError
try:
f = Fraction(1, 0) # 分母为 0,触发 ValueError
except ValueError as e:
print(f"创建失败:{e}") # 创建失败:分母不能为 0
finally:
print("无论是否出错,finally 都会执行") # 用于释放资源、收尾等

# 2. 捕获类型不符的 TypeError
try:
f1 = Fraction(1, 2)
result = f1 + 3 # 3 不是 Fraction,触发 TypeError
except TypeError as e:
print(f"运算失败:{e}") # 运算失败:unsupported operand type(s)…
finally:
print("运算流程结束")

# 3. 正常情况 + finally 收尾
try:
f1 = Fraction(1, 2)
f2 = Fraction(1, 3)
print(f1 + f2) # 5/6,正常执行
except (ValueError, TypeError) as e:
print(f"出错了:{e}")
finally:
print("无论成功还是失败,finally 都会执行")

处理思路说明:

  • ValueError:分母为 0 属于「业务逻辑上的非法值」,用 except ValueError 捕获,并给出友好提示,避免程序直接崩溃。
  • TypeError:运算对象类型不符属于「类型层面的错误」,用 except TypeError 捕获,提示调用方传入正确的 Fraction 对象。
  • finally:无论是否发生异常都会执行,适合做资源清理、日志记录等收尾工作;也可以把多个异常类型写在同一个 except 元组里统一处理。

6.4 让对象可迭代:__iter__ 与 __getitem__

class Countdown:
def __init__(self, start):
self.start = start

def __iter__(self):
"""返回迭代器,支持 for 循环"""
n = self.start
while n > 0:
yield n
n -= 1

for num in Countdown(5):
print(num, end=" ") # 5 4 3 2 1

7. 综合实战:员工管理系统

把本章知识串起来,实现一个简单的员工管理系统:

class Employee:
"""员工基类"""
def __init__(self, name, salary):
self.name = name
self.salary = salary

def work(self):
return f"{self.name} 正在工作"

def get_salary(self):
return self.salary

def __str__(self):
return f"{self.name}(月薪 {self.salary} 元)"

class Developer(Employee):
"""程序员:继承员工,重写 work"""
def __init__(self, name, salary, language):
super().__init__(name, salary)
self.language = language

def work(self):
return f"{self.name} 正在用 {self.language} 写代码"

def __add__(self, other):
"""两个程序员合并薪资(演示魔术方法)"""
return Developer(f"{self.name}&{other.name}", self.salary + other.salary, self.language)

class Manager(Employee):
"""经理:继承员工,重写 work"""
def __init__(self, name, salary, team_size):
super().__init__(name, salary)
self.team_size = team_size

def work(self):
return f"{self.name} 正在管理 {self.team_size} 人的团队"

# 多态:统一调用 work()
employees = [
Developer("张三", 20000, "Python"),
Developer("李四", 18000, "Java"),
Manager("王五", 35000, 10),
]

for emp in employees:
print(emp.work())

# 输出:
# 张三 正在用 Python 写代码
# 李四 正在用 Java 写代码
# 王五 正在管理 10 人的团队

# 魔术方法演示
dev1 = Developer("张三", 20000, "Python")
dev2 = Developer("李四", 18000, "Python")
team = dev1 + dev2
print(team) # 张三&李四(月薪 38000 元)
print(team.work()) # 张三&李四 正在用 Python 写代码

8. 总结

本章我们系统学习了 Python 面向对象编程的核心知识:

  • 类与对象:类是模板,对象是实例;__init__ 负责初始化,self 代表实例本身。
  • 封装:通过私有属性(__xxx)和 @property 保护数据,对外只暴露必要接口。
  • 继承:子类复用父类代码,用 super() 调用父类方法,支持多继承。
  • 多态:同一方法在不同对象上表现不同,配合鸭子类型让代码更灵活。
  • 魔术方法:通过 __str__、__add__、__iter__ 等定制类的行为,让自定义类更贴近 Python 内置类型的使用体验。

面向对象是 Python 进阶的重要基石。下一篇我们将继续深入探讨更多高级特性,敬请期待!

赞(0)
未经允许不得转载:171主机测评 » Python 基础语法(四):类与对象、封装/继承/多态、魔术方法
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址