面向对象编程
面向对象编程简介
面向对象编程(Object-Oriented Programming,简称OOP)是一种程序设计范式,它将程序中的数据和操作数据的方法组织成对象,通过对象之间的交互来完成程序功能。
为什么要学习面向对象编程?
- 更好的代码组织方式,提高代码的可维护性
- 代码重用性强,减少重复代码
- 更接近人类思维方式,易于理解和使用
- 适合开发大型复杂的程序
类与对象
类的定义
类是对象的模板,它定义了对象的属性和方法。在Python中使用class关键字定义类:
class Student:
"""学生类"""
def __init__(self, name, age):
self.name = name # 实例属性
self.age = age
def introduce(self): # 实例方法
return f"我叫{self.name},今年{self.age}岁"
对象的创建
对象是类的实例,通过类名后跟括号的方式创建:
# 创建Student类的实例
student1 = Student("张三", 18)
student2 = Student("李四", 19)
# 调用对象的方法
print(student1.introduce()) # 输出:我叫张三,今年18岁
print(student2.introduce()) # 输出:我叫李四,今年19岁
属性与方法
实例属性
实例属性是属于对象的变量,每个对象都有自己的一份独立的实例属性:
class Dog:
def __init__(self, name):
self.name = name # 实例属性
self.tricks = [] # 另一个实例属性
def add_trick(self, trick):
self.tricks.append(trick)
# 创建两个Dog实例
dog1 = Dog("旺财")
dog2 = Dog("来福")
# 给狗狗教不同的技能
dog1.add_trick("握手")
dog2.add_trick("打滚")
print(dog1.tricks) # ['握手']
print(dog2.tricks) # ['打滚']
类属性
类属性是属于类的变量,该类的所有实例共享同一个类属性:
class Student:
school = "Python学习网" # 类属性
def __init__(self, name):
self.name = name # 实例属性
# 创建实例
student1 = Student("张三")
student2 = Student("李四")
# 访问类属性
print(Student.school) # Python学习网
print(student1.school) # Python学习网
print(student2.school) # Python学习网
# 修改类属性
Student.school = "新Python学习网"
print(student1.school) # 新Python学习网
print(student2.school) # 新Python学习网
继承与多态
继承基础
继承允许我们基于一个类创建新类,新类继承了原有类的属性和方法:
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
pass
class Dog(Animal):
def speak(self):
return f"{self.name}说:汪汪!"
class Cat(Animal):
def speak(self):
return f"{self.name}说:喵喵!"
# 创建实例
dog = Dog("旺财")
cat = Cat("咪咪")
print(dog.speak()) # 旺财说:汪汪!
print(cat.speak()) # 咪咪说:喵喵!
方法重写
子类可以重写(覆盖)父类的方法,以实现自己的特定行为。如果需要调用父类的方法,可以使用super()函数:
class Vehicle:
def __init__(self, brand, model):
self.brand = brand
self.model = model
def start_engine(self):
return f"{self.brand} {self.model}的发动机启动了"
def show_info(self):
return f"这是一辆{self.brand} {self.model}"
class ElectricCar(Vehicle):
def __init__(self, brand, model, battery_capacity):
# 调用父类的__init__方法
super().__init__(brand, model)
self.battery_capacity = battery_capacity
def start_engine(self):
# 完全重写父类的方法
return f"{self.brand} {self.model}的电机启动了,电池容量{self.battery_capacity}kWh"
def show_info(self):
# 在父类方法基础上添加新功能
basic_info = super().show_info()
return f"{basic_info},使用电力驱动"
# 创建实例
car = Vehicle("丰田", "凯美瑞")
tesla = ElectricCar("特斯拉", "Model 3", 75)
# 调用方法
print(car.start_engine()) # 丰田 凯美瑞的发动机启动了
print(tesla.start_engine()) # 特斯拉 Model 3的电机启动了,电池容量75kWh
print(tesla.show_info()) # 这是一辆特斯拉 Model 3,使用电力驱动
方法重写的三种常见方式
- 完全重写:子类完全覆盖父类的方法实现
- 部分重写:调用父类方法,并添加新功能
- 选择性重写:根据条件决定是否调用父类方法
多态性
多态性允许我们以统一的方式处理不同类型的对象,只要这些对象都是某个共同基类的子类:
class Shape:
def area(self):
pass
def describe(self):
return f"这是一个{self.__class__.__name__}"
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
def area(self):
return 3.14 * self.radius ** 2
class Rectangle(Shape):
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
return self.width * self.height
def print_shape_info(shape):
"""多态函数:接受任何Shape类型的对象"""
print(shape.describe())
print(f"面积是: {shape.area()}")
# 创建不同的形状
shapes = [
Circle(5),
Rectangle(4, 6),
Circle(3)
]
# 统一处理不同类型的对象
for shape in shapes:
print_shape_info(shape)
print("---")
Python的鸭子类型
Python支持"鸭子类型":如果一个对象实现了某个方法,我们就可以使用它, 而不需要关心它的具体类型。这提供了更灵活的多态性:
class Duck:
def speak(self):
return "嘎嘎!"
class Cat:
def speak(self):
return "喵喵!"
class Person:
def speak(self):
return "你好!"
def make_speak(thing):
# 不关心对象类型,只要有speak方法就可以调用
print(thing.speak())
# 使用鸭子类型
things = [Duck(), Cat(), Person()]
for thing in things:
make_speak(thing)
注意事项
- 确保子类方法的参数与父类方法兼容
- 重写方法时保持接口的一致性
- 适当使用抽象基类来强制接口规范
封装与抽象
私有属性
在Python中,通过在属性名前加双下划线__来创建私有属性,这样的属性只能在类的内部访问:
class BankAccount:
def __init__(self, owner, balance):
self.owner = owner # 公有属性
self.__balance = balance # 私有属性
def deposit(self, amount):
if amount > 0:
self.__balance += amount
return True
return False
def get_balance(self):
return self.__balance
# 创建账户
account = BankAccount("张三", 1000)
# 正确的访问方式
print(account.owner) # 可以直接访问公有属性
account.deposit(500) # 通过方法访问私有属性
print(account.get_balance()) # 输出:1500
# 错误的访问方式
# print(account.__balance) # 这会引发AttributeError错误
名称改写
Python中的私有属性实际上是通过名称改写(name mangling)实现的。 双下划线开头的属性会被改写为_类名__属性名的形式。这不是真正的私有, 但这种约定俗成的方式可以有效防止属性被意外访问或修改。
属性装饰器
使用@property装饰器可以将方法转换为属性,实现更优雅的属性访问和控制:
class Temperature:
def __init__(self, celsius):
self.__celsius = celsius
@property
def celsius(self):
"""获取摄氏温度"""
return self.__celsius
@celsius.setter
def celsius(self, value):
"""设置摄氏温度"""
if value < -273.15: # 验证温度不低于绝对零度
raise ValueError("温度不能低于绝对零度")
self.__celsius = value
@property
def fahrenheit(self):
"""获取华氏温度"""
return self.__celsius * 9/5 + 32
@fahrenheit.setter
def fahrenheit(self, value):
"""设置华氏温度"""
self.celsius = (value - 32) * 5/9
# 使用示例
temp = Temperature(25)
print(temp.celsius) # 25
print(temp.fahrenheit) # 77.0
temp.celsius = 30 # 使用setter设置温度
print(temp.fahrenheit) # 86.0
# temp.celsius = -300 # 这会引发ValueError
抽象基类(ABC)
抽象基类用于定义接口规范,它可以强制子类实现特定的方法。Python通过abc模块提供抽象基类支持:
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self):
"""计算面积的抽象方法"""
pass
@abstractmethod
def perimeter(self):
"""计算周长的抽象方法"""
pass
def describe(self):
"""普通方法(非抽象方法)"""
return f"这是一个{self.__class__.__name__},面积为{self.area()}"
# 尝试实例化抽象类会引发错误
# shape = Shape() # TypeError: Can't instantiate abstract class Shape
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
def area(self): # 必须实现area方法
return 3.14 * self.radius ** 2
def perimeter(self): # 必须实现perimeter方法
return 2 * 3.14 * self.radius
# 正确使用示例
circle = Circle(5)
print(circle.area()) # 78.5
print(circle.describe()) # 这是一个Circle,面积为78.5
抽象基类的作用
- 定义接口规范,确保子类实现必要的方法
- 提供代码复用的基础架构
- 支持类型检查和验证
抽象基类还可以包含抽象属性和具体实现:
from abc import ABC, abstractmethod, abstractproperty
class Vehicle(ABC):
@abstractmethod
def start_engine(self):
"""启动引擎"""
pass
@abstractproperty
def fuel_type(self):
"""燃料类型"""
pass
def stop_engine(self):
"""关闭引擎(具体实现)"""
return "引擎已关闭"
class ElectricCar(Vehicle):
@property
def fuel_type(self):
return "电力"
def start_engine(self):
return "电机启动"
class GasCar(Vehicle):
@property
def fuel_type(self):
return "汽油"
def start_engine(self):
return "汽油发动机启动"
# 使用示例
tesla = ElectricCar()
toyota = GasCar()
print(tesla.fuel_type) # 电力
print(toyota.fuel_type) # 汽油
print(tesla.start_engine()) # 电机启动
print(toyota.stop_engine()) # 引擎已关闭
注意事项
- 抽象方法必须被子类实现,否则子类也会变成抽象类
- 抽象基类可以包含具体方法的实现
- 使用@abstractmethod而不是@abstractproperty(已弃用)
实际应用示例:数据库接口
from abc import ABC, abstractmethod
class Database(ABC):
@abstractmethod
def connect(self):
"""连接数据库"""
pass
@abstractmethod
def disconnect(self):
"""断开连接"""
pass
@abstractmethod
def execute(self, query):
"""执行查询"""
pass
def transaction(self, queries):
"""事务处理(具体实现)"""
try:
for query in queries:
self.execute(query)
return True
except Exception as e:
print(f"事务失败: {e}")
return False
class MySQLDatabase(Database):
def connect(self):
return "连接到MySQL数据库"
def disconnect(self):
return "断开MySQL连接"
def execute(self, query):
return f"在MySQL中执行: {query}"
class PostgreSQLDatabase(Database):
def connect(self):
return "连接到PostgreSQL数据库"
def disconnect(self):
return "断开PostgreSQL连接"
def execute(self, query):
return f"在PostgreSQL中执行: {query}"
# 使用示例
def process_data(database: Database):
database.connect()
database.execute("SELECT * FROM users")
database.disconnect()
# 可以使用任何Database的子类
mysql_db = MySQLDatabase()
postgres_db = PostgreSQLDatabase()
process_data(mysql_db)
process_data(postgres_db)
魔术方法
__init__方法
__init__是最常用的魔术方法,用于初始化对象:
class Point:
def __init__(self, x=0, y=0):
"""初始化方法,创建点的坐标"""
self.x = x
self.y = y
# 创建点对象
p1 = Point() # 使用默认值 (0, 0)
p2 = Point(3, 4) # 指定坐标 (3, 4)
__str__和__repr__
这两个方法用于对象的字符串表示:
class Point:
def __init__(self, x=0, y=0):
self.x = x
self.y = y
def __str__(self):
"""返回对象的字符串表示,面向用户"""
return f"点({self.x}, {self.y})"
def __repr__(self):
"""返回对象的详细字符串表示,面向开发者"""
return f"Point(x={self.x}, y={self.y})"
p = Point(3, 4)
print(str(p)) # 输出:点(3, 4)
print(repr(p)) # 输出:Point(x=3, y=4)
其他魔术方法
Python提供了许多其他魔术方法来自定义对象的行为:
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
"""实现向量加法:+运算符"""
return Vector(self.x + other.x, self.y + other.y)
def __sub__(self, other):
"""实现向量减法:-运算符"""
return Vector(self.x - other.x, self.y - other.y)
def __len__(self):
"""实现len()函数"""
return int((self.x**2 + self.y**2)**0.5)
def __str__(self):
return f"Vector({self.x}, {self.y})"
# 使用示例
v1 = Vector(2, 3)
v2 = Vector(3, 4)
v3 = v1 + v2 # 使用+运算符
print(v3) # Vector(5, 7)
print(len(v1)) # 3 (向量长度的整数部分)
常用魔术方法
- __eq__(self, other): 定义==运算符
- __lt__(self, other): 定义<运算符
- __getitem__(self, key): 定义索引访问
- __call__(self, *args): 使对象可调用
- __enter__和__exit__: 上下文管理器
练习与实践
难度说明
- 基础练习:巩固基本概念
- 进阶练习:综合应用多个概念
- 挑战练习:需要深入思考和创新
练习1:创建图书类
创建一个Book类,包含以下要求:
- 属性:书名、作者、价格、库存量
- 方法:
- 显示图书信息
- 更新库存量
- 计算打折后的价格
提示:使用property装饰器处理价格的获取和设置,确保价格不能为负数
参考代码结构:
class Book:
def __init__(self, title, author, price, stock):
self.title = title
self.author = author
self._price = price # 使用下划线表示protected属性
self.stock = stock
@property
def price(self):
# 在这里实现价格的getter方法
pass
@price.setter
def price(self, value):
# 在这里实现价格的setter方法
pass
练习2:银行账户系统
设计一个简单的银行账户系统,包含以下类:
- Account(基类):基本账户操作
- SavingAccount(储蓄账户):包含利息计算
- CheckingAccount(支票账户):包含透支额度
提示:使用继承实现不同类型的账户,确保账户余额不会出现非法操作
测试用例:
# 创建账户
savings = SavingAccount("张三", 1000, 0.05) # 年利率5%
checking = CheckingAccount("李四", 2000, 500) # 透支额度500
# 测试存款和取款
savings.deposit(500)
assert savings.balance == 1500
checking.withdraw(2300) # 允许透支
练习3:商品库存管理
实现一个商品库存管理系统,要求:
- 创建Product基类和不同类型的商品子类(如Electronics、Clothing等)
- 实现库存管理功能(入库、出库、库存查询)
- 使用property装饰器管理商品属性
- 实现商品搜索和分类功能
提示:考虑使用类方法和静态方法来实现一些通用功能
示例实现:
class Product:
_all_products = [] # 类属性,存储所有商品
def __init__(self, name, price, stock):
self.name = name
self._price = price
self._stock = stock
Product._all_products.append(self)
@classmethod
def search(cls, keyword):
"""搜索商品"""
return [p for p in cls._all_products if keyword in p.name]
@staticmethod
def validate_price(price):
"""验证价格是否合法"""
return price > 0
练习4:游戏角色系统
设计一个游戏角色系统,包含以下功能:
- 创建Character基类和不同职业的子类(如Warrior、Mage、Archer)
- 实现角色属性(生命值、魔法值、攻击力等)
- 设计技能系统(每个职业有特殊技能)
- 实现角色交互(攻击、防御、使用技能等)
- 添加状态效果系统(如中毒、眩晕等)
提示:使用多重继承或混入类(Mixin)来实现状态效果系统
示例代码结构:
class Character:
def __init__(self, name, health, mana, attack):
self.name = name
self._health = health
self._mana = mana
self._attack = attack
self._effects = [] # 状态效果列表
def use_skill(self, skill, target):
if self._mana >= skill.mana_cost:
self._mana -= skill.mana_cost
return skill.execute(self, target)
return "魔法值不足!"
class Warrior(Character):
def __init__(self, name):
super().__init__(name, health=100, mana=50, attack=15)
self.skills = [
Skill("冲锋", 10, self._charge),
Skill("旋风斩", 20, self._whirlwind)
]
def _charge(self, target):
# 实现冲锋技能
pass
练习5:自定义集合类
实现一个自定义的集合类,要求:
- 支持基本的集合操作(添加、删除、查找等)
- 实现迭代器协议(__iter__和__next__)
- 支持运算符重载(并集+、交集&、差集-)
- 实现长度计算和成员检测
- 支持切片操作
提示:需要实现多个魔术方法来支持这些功能
测试代码:
# 创建自定义集合
s1 = MySet([1, 2, 3, 4])
s2 = MySet([3, 4, 5, 6])
# 测试运算符重载
s3 = s1 + s2 # 并集
s4 = s1 & s2 # 交集
s5 = s1 - s2 # 差集
# 测试迭代和切片
for item in s1:
print(item)
print(s1[1:3]) # 切片访问