Python面向对象详解
1. 概述
面向对象编程(Object-Oriented Programming, OOP)是一种编程范式,它将数据和操作数据的方法封装在一起,形成对象,并通过对象之间的交互来构建程序。Python是一种支持面向对象编程的高级编程语言,它提供了丰富的面向对象特性。
1.1 面向对象的核心概念
- 对象(Object):客观世界中的实体,具有属性(数据)和方法(操作)
- 类(Class):对象的模板或蓝图,定义了对象的属性和方法
- 实例化(Instantiation):根据类创建对象的过程
- 继承(Inheritance):一个类继承另一个类的属性和方法
- 多态(Polymorphism):不同类型的对象可以以相同的方式响应相同的消息
- 封装(Encapsulation):将数据和方法封装在对象内部,对外部隐藏实现细节
- 抽象(Abstraction):只展示对象的必要特性,隐藏复杂的实现细节
1.2 Python面向对象的特点
- Python支持完整的面向对象编程特性
- 所有东西都是对象,包括数字、字符串、函数等
- 支持多继承
- 支持动态类型
- 支持运算符重载
- 语法简洁,易于学习和使用
2. 类和对象
2.1 定义类
在Python中,使用class关键字定义类:
# 定义一个简单的类
class Person:
# 类属性(所有实例共享)
species = "人类"
# 构造方法
def __init__(self, name, age):
# 实例属性(每个实例独有)
self.name = name
self.age = age
# 实例方法
def greet(self):
print(f"你好,我叫{self.name},今年{self.age}岁。")
# 实例方法
def have_birthday(self):
self.age += 1
print(f"生日快乐!{self.name}现在{self.age}岁了。")
2.2 创建对象(实例化)
# 创建Person类的实例
person1 = Person("张三", 30)
person2 = Person("李四", 25)
# 访问实例属性
print(person1.name) # 输出: 张三
print(person2.age) # 输出: 25
# 调用实例方法
person1.greet() # 输出: 你好,我叫张三,今年30岁。
person2.have_birthday() # 输出: 生日快乐!李四现在26岁了。
# 访问类属性
print(Person.species) # 输出: 人类
print(person1.species) # 输出: 人类
print(person2.species) # 输出: 人类
# 修改类属性
Person.species = "智人"
print(person1.species) # 输出: 智人
2.3 __init__构造方法
__init__方法是类的构造方法,在创建对象时自动调用:
- 用于初始化对象的属性
- 第一个参数必须是
self,代表当前实例 - 可以有多个参数,用于接收初始化数据
- 不需要显式返回值
class Car:
def __init__(self, brand, model, year):
self.brand = brand
self.model = model
self.year = year
self.mileage = 0 # 默认属性
def drive(self, distance):
self.mileage += distance
print(f"行驶了{distance}公里,总里程:{self.mileage}公里。")
# 创建Car实例
my_car = Car("Toyota", "Camry", 2020)
print(f"我的车:{my_car.brand} {my_car.model} {my_car.year}")
my_car.drive(100)
2.4 __del__析构方法
__del__方法是类的析构方法,在对象被销毁时自动调用:
- 用于清理资源
- 不推荐依赖析构方法来管理资源
class FileHandler:
def __init__(self, filename):
self.filename = filename
self.file = open(filename, "r")
print(f"打开文件:{filename}")
def read_content(self):
return self.file.read()
def __del__(self):
self.file.close()
print(f"关闭文件:{self.filename}")
# 创建实例
handler = FileHandler("example.txt")
content = handler.read_content()
print(content)
# 销毁实例
del handler
3. 属性
3.1 实例属性
实例属性是每个对象独有的属性,在__init__方法或其他实例方法中定义:
class Dog:
def __init__(self, name, breed):
self.name = name # 实例属性
self.breed = breed # 实例属性
self.age = 0 # 实例属性(默认值)
def grow_up(self):
self.age += 1 # 在实例方法中修改实例属性
# 创建两个Dog实例
dog1 = Dog("Buddy", "Golden Retriever")
dog2 = Dog("Max", "German Shepherd")
# 每个实例都有自己的属性
dog1.grow_up()
print(f"{dog1.name} ({dog1.breed}):{dog1.age}岁") # 输出: Buddy (Golden Retriever):1岁
print(f"{dog2.name} ({dog2.breed}):{dog2.age}岁") # 输出: Max (German Shepherd):0岁
3.2 类属性
类属性是类的所有实例共享的属性,在类定义中直接定义:
class Student:
# 类属性
school_name = "北京大学"
student_count = 0
def __init__(self, name, major):
self.name = name # 实例属性
self.major = major # 实例属性
Student.student_count += 1 # 访问类属性
# 创建Student实例
student1 = Student("张三", "计算机科学")
student2 = Student("李四", "物理学")
# 所有实例共享类属性
print(student1.school_name) # 输出: 北京大学
print(student2.school_name) # 输出: 北京大学
print(Student.school_name) # 输出: 北京大学
# 修改类属性
Student.school_name = "清华大学"
print(student1.school_name) # 输出: 清华大学
print(student2.school_name) # 输出: 清华大学
# 学生总数
print(f"学生总数:{Student.student_count}") # 输出: 学生总数:2
3.3 属性访问控制
Python通过命名约定来实现属性的访问控制:
- 公开属性:默认,任何地方都可以访问
- 受保护属性:以单下划线
_开头,不应该被外部直接访问(但技术上可以) - 私有属性:以双下划线
__开头,会被Python重命名,外部无法直接访问
class BankAccount:
def __init__(self, account_number, balance):
self.account_number = account_number # 公开属性
self._balance = balance # 受保护属性
self.__pin = 1234 # 私有属性
def deposit(self, amount):
if amount > 0:
self._balance += amount
print(f"存款{amount}元,余额:{self._balance}元")
def withdraw(self, amount, pin):
if pin == self.__pin and amount > 0 and amount <= self._balance:
self._balance -= amount
print(f"取款{amount}元,余额:{self._balance}元")
else:
print("取款失败")
def get_balance(self):
return self._balance
# 创建BankAccount实例
account = BankAccount("1234567890", 1000)
# 访问公开属性
print(f"账号:{account.account_number}")
# 访问受保护属性(不推荐)
print(f"余额(直接访问):{account._balance}")
# 通过方法访问受保护属性(推荐)
print(f"余额(方法访问):{account.get_balance()}")
# 尝试访问私有属性(会引发AttributeError)
try:
print(f"PIN码:{account.__pin}")
except AttributeError as e:
print(f"错误:{e}")
# 实际存储的私有属性名称(Python重命名)
print(f"实际私有属性名:{account._BankAccount__pin}") # 输出: 实际私有属性名:1234
# 使用方法操作账户
account.deposit(500)
account.withdraw(300, 1234)
account.withdraw(300, 0000) # 密码错误
3.4 属性装饰器
Python提供了@property、@property.setter和@property.deleter装饰器来控制属性的访问:
class Person:
def __init__(self, name, age):
self._name = name
self._age = age
@property
def name(self):
"""获取姓名"""
return self._name
@property
def age(self):
"""获取年龄"""
return self._age
@age.setter
def age(self, value):
"""设置年龄,确保年龄为正整数"""
if isinstance(value, int) and value > 0:
self._age = value
else:
raise ValueError("年龄必须是正整数")
@age.deleter
def age(self):
"""删除年龄属性"""
del self._age
# 创建Person实例
person = Person("张三", 30)
# 访问属性(通过property)
print(f"姓名:{person.name}")
print(f"年龄:{person.age}")
# 设置属性(通过property.setter)
try:
person.age = 31
print(f"新年龄:{person.age}")
person.age = -5 # 无效年龄
except ValueError as e:
print(f"错误:{e}")
# 删除属性(通过property.deleter)
del person.age
try:
print(person.age)
except AttributeError:
print("年龄属性已删除")
4. 方法
4.1 实例方法
实例方法是最常用的方法类型,第一个参数必须是self,代表当前实例:
class Circle:
def __init__(self, radius):
self.radius = radius
# 实例方法
def area(self):
"""计算圆的面积"""
return 3.14159 * self.radius ** 2
# 实例方法
def circumference(self):
"""计算圆的周长"""
return 2 * 3.14159 * self.radius
# 创建Circle实例
circle = Circle(5)
# 调用实例方法
print(f"半径:{circle.radius}")
print(f"面积:{circle.area()}")
print(f"周长:{circle.circumference()}")
4.2 类方法
类方法使用@classmethod装饰器定义,第一个参数必须是cls,代表当前类:
class Date:
def __init__(self, year, month, day):
self.year = year
self.month = month
self.day = day
def __str__(self):
return f"{self.year}-{self.month:02d}-{self.day:02d}"
# 类方法
@classmethod
def from_string(cls, date_string):
"""从字符串创建Date对象"""
year, month, day = map(int, date_string.split("-"))
return cls(year, month, day) # 调用构造方法
# 类方法
@classmethod
def today(cls):
"""获取当前日期"""
from datetime import datetime
now = datetime.now()
return cls(now.year, now.month, now.day)
# 使用构造方法创建Date实例
date1 = Date(2023, 5, 20)
print(date1)
# 使用类方法创建Date实例
date2 = Date.from_string("2023-05-21")
print(date2)
# 使用类方法获取当前日期
date3 = Date.today()
print(date3)
4.3 静态方法
静态方法使用@staticmethod装饰器定义,没有self或cls参数,与类和实例无关:
class MathUtils:
# 静态方法
@staticmethod
def add(a, b):
"""加法"""
return a + b
# 静态方法
@staticmethod
def multiply(a, b):
"""乘法"""
return a * b
# 静态方法
@staticmethod
def is_even(number):
"""判断是否为偶数"""
return number % 2 == 0
# 直接通过类调用静态方法
print(f"1 + 2 = {MathUtils.add(1, 2)}")
print(f"3 * 4 = {MathUtils.multiply(3, 4)}")
print(f"5是偶数吗?{MathUtils.is_even(5)}")
print(f"6是偶数吗?{MathUtils.is_even(6)}")
# 也可以通过实例调用静态方法(不推荐)
math_utils = MathUtils()
print(f"7 + 8 = {math_utils.add(7, 8)}")
5. 继承
继承是面向对象编程的重要特性,它允许一个类继承另一个类的属性和方法:
5.1 基本继承
# 父类(基类)
class Animal:
def __init__(self, name):
self.name = name
def eat(self):
print(f"{self.name}正在吃东西")
def sleep(self):
print(f"{self.name}正在睡觉")
# 子类(派生类)继承Animal
class Dog(Animal):
def __init__(self, name, breed):
super().__init__(name) # 调用父类的构造方法
self.breed = breed
def bark(self):
print(f"{self.name}({self.breed})正在汪汪叫")
# 子类(派生类)继承Animal
class Cat(Animal):
def __init__(self, name, color):
super().__init__(name) # 调用父类的构造方法
self.color = color
def meow(self):
print(f"{self.name}({self.color})正在喵喵叫")
# 创建Dog实例
dog = Dog("Buddy", "Golden Retriever")
dog.eat() # 继承自父类
dog.sleep() # 继承自父类
dog.bark() # 子类自己的方法
# 创建Cat实例
cat = Cat("Kitty", "白色")
cat.eat() # 继承自父类
cat.sleep() # 继承自父类
cat.meow() # 子类自己的方法
5.2 方法重写
子类可以重写父类的方法,实现自己的行为:
class Animal:
def __init__(self, name):
self.name = name
def make_sound(self):
print(f"{self.name}发出声音")
class Dog(Animal):
def make_sound(self):
print(f"{self.name}汪汪叫")
class Cat(Animal):
def make_sound(self):
print(f"{self.name}喵喵叫")
class Cow(Animal):
def make_sound(self):
print(f"{self.name}哞哞叫")
# 创建实例
animals = [Dog("Buddy"), Cat("Kitty"), Cow("Bessie")]
# 多态的体现:不同对象以相同方式响应相同消息
for animal in animals:
animal.make_sound()
5.3 多继承
Python支持多继承,一个类可以继承多个父类:
class Flyable:
def fly(self):
print("可以飞行")
class Swimmable:
def swim(self):
print("可以游泳")
# 多继承:Duck同时继承Flyable和Swimmable
class Duck(Flyable, Swimmable):
def quack(self):
print("嘎嘎叫")
# 创建Duck实例
duck = Duck()
duck.fly() # 继承自Flyable
duck.swim() # 继承自Swimmable
duck.quack() # 自己的方法
5.4 继承顺序(MRO)
Python使用C3线性化算法确定多继承的方法解析顺序(Method Resolution Order, MRO):
class A:
def say(self):
print("A")
class B(A):
def say(self):
print("B")
class C(A):
def say(self):
print("C")
class D(B, C):
pass
# 创建D实例
d = D()
d.say() # 输出: B
# 查看MRO
print(D.__mro__) # 输出: (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
# 使用super()遵循MRO
class D(B, C):
def say(self):
super().say() # 调用MRO中的下一个类的方法
# 创建新的D实例
d = D()
d.say() # 输出: B
6. 多态
多态是指不同类型的对象可以以相同的方式响应相同的消息:
class Shape:
def area(self):
raise NotImplementedError("子类必须实现area方法")
class Rectangle(Shape):
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
return self.width * self.height
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
def area(self):
return 3.14159 * self.radius ** 2
class Triangle(Shape):
def __init__(self, base, height):
self.base = base
self.height = height
def area(self):
return 0.5 * self.base * self.height
# 计算不同形状的面积
def calculate_total_area(shapes):
total = 0
for shape in shapes:
total += shape.area() # 多态:不同对象调用相同的area方法
return total
# 创建不同形状的实例
shapes = [
Rectangle(5, 10),
Circle(7),
Triangle(8, 6)
]
# 计算总面积
total_area = calculate_total_area(shapes)
print(f"总面积:{total_area}")
7. 封装
封装是指将数据和方法封装在对象内部,对外部隐藏实现细节:
class Student:
def __init__(self, name, student_id):
self._name = name # 受保护属性
self._student_id = student_id # 受保护属性
self._grades = {} # 受保护属性
def add_grade(self, course, grade):
"""添加成绩"""
if 0 <= grade <= 100:
self._grades[course] = grade
else:
raise ValueError("成绩必须在0-100之间")
def get_grade(self, course):
"""获取指定课程的成绩"""
return self._grades.get(course, "未选修该课程")
def get_average_grade(self):
"""获取平均成绩"""
if not self._grades:
return 0
return sum(self._grades.values()) / len(self._grades)
@property
def name(self):
"""获取姓名"""
return self._name
@property
def student_id(self):
"""获取学号"""
return self._student_id
# 创建Student实例
student = Student("张三", "2023001")
# 设置成绩
student.add_grade("数学", 90)
student.add_grade("英语", 85)
student.add_grade("计算机", 95)
# 获取成绩
print(f"姓名:{student.name}")
print(f"学号:{student.student_id}")
print(f"数学成绩:{student.get_grade('数学')}")
print(f"平均成绩:{student.get_average_grade()}")
# 尝试直接修改属性(不推荐)
student._grades["数学"] = 110 # 绕过了验证
print(f"数学成绩(直接修改):{student.get_grade('数学')}")
8. 特殊方法
Python提供了许多特殊方法(也称为魔术方法),用于实现对象的特殊行为:
8.1 字符串表示方法
__str__:返回对象的字符串表示,用于print()和str()__repr__:返回对象的官方字符串表示,用于调试
class Book:
def __init__(self, title, author, year):
self.title = title
self.author = author
self.year = year
def __str__(self):
return f"《{self.title}》- {self.author} ({self.year})
def __repr__(self):
return f"Book(title='{self.title}', author='{self.author}', year={self.year})
# 创建Book实例
book = Book("Python编程", "张荣殿", 2023)
# 使用print()调用__str__
print(book) # 输出: 《Python编程》- 张荣殿 (2023)
# 使用str()调用__str__
print(str(book)) # 输出: 《Python编程》- 张荣殿 (2023)
# 使用repr()调用__repr__
print(repr(book)) # 输出: Book(title='Python编程', author='张荣殿', year=2023)
# 在交互式环境中直接输入对象会调用__repr__
# >>> book
# Book(title='Python编程', author='张荣殿', year=2023)
8.2 比较方法
__eq__:等于(==)__ne__:不等于(!=)__lt__:小于(<)__le__:小于等于(<=)__gt__:大于(>)__ge__:大于等于(>=)
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __eq__(self, other):
"""等于"""
if not isinstance(other, Point):
return NotImplemented
return self.x == other.x and self.y == other.y
def __lt__(self, other):
"""小于"""
if not isinstance(other, Point):
return NotImplemented
return (self.x, self.y) < (other.x, other.y)
def __str__(self):
return f"({self.x}, {self.y})"
# 创建Point实例
p1 = Point(1, 2)
p2 = Point(1, 2)
p3 = Point(3, 4)
# 比较相等
print(f"p1 == p2: {p1 == p2}") # 输出: True
print(f"p1 == p3: {p1 == p3}") # 输出: False
# 比较大小
print(f"p1 < p3: {p1 < p3}") # 输出: True
print(f"p3 < p1: {p3 < p1}") # 输出: False
# 排序
points = [p3, p1, Point(0, 5), Point(2, 1)]
points.sort()
print(f"排序后的点:{[str(p) for p in points]}")
8.3 算术运算方法
__add__:加法(+)__sub__:减法(-)__mul__:乘法(*)__truediv__:除法(/)__floordiv__:整除(//)__mod__:取模(%)
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
"""向量加法"""
if not isinstance(other, Vector):
return NotImplemented
return Vector(self.x + other.x, self.y + other.y)
def __sub__(self, other):
"""向量减法"""
if not isinstance(other, Vector):
return NotImplemented
return Vector(self.x - other.x, self.y - other.y)
def __mul__(self, scalar):
"""向量乘法(与标量)"""
if not isinstance(scalar, (int, float)):
return NotImplemented
return Vector(self.x * scalar, self.y * scalar)
def __str__(self):
return f"Vector({self.x}, {self.y})"
# 创建Vector实例
v1 = Vector(1, 2)
v2 = Vector(3, 4)
# 向量加法
v3 = v1 + v2
print(f"v1 + v2 = {v3}")
# 向量减法
v4 = v2 - v1
print(f"v2 - v1 = {v4}")
# 向量乘法(与标量)
v5 = v1 * 3
print(f"v1 * 3 = {v5}")
8.4 容器类型方法
__len__:获取长度(len())__getitem__:获取元素([])__setitem__:设置元素([] =)__delitem__:删除元素(del)__contains__:检查包含(in)
class CustomList:
def __init__(self, *items):
self._items = list(items)
def __len__(self):
"""获取长度"""
return len(self._items)
def __getitem__(self, index):
"""获取元素"""
return self._items[index]
def __setitem__(self, index, value):
"""设置元素"""
self._items[index] = value
def __delitem__(self, index):
"""删除元素"""
del self._items[index]
def __contains__(self, item):
"""检查包含"""
return item in self._items
def __str__(self):
return str(self._items)
# 创建CustomList实例
my_list = CustomList(1, 2, 3, 4, 5)
# 获取长度
print(f"长度:{len(my_list)}")
# 获取元素
print(f"第一个元素:{my_list[0]}")
print(f"最后一个元素:{my_list[-1]}")
# 设置元素
my_list[2] = 10
print(f"修改后的列表:{my_list}")
# 删除元素
del my_list[1]
print(f"删除后的列表:{my_list}")
# 检查包含
print(f"10在列表中吗?{10 in my_list}")
print(f"2在列表中吗?{2 in my_list}")
9. 抽象类和接口
Python使用abc模块实现抽象类和接口:
9.1 抽象类
from abc import ABC, abstractmethod
class Shape(ABC):
"""抽象类Shape"""
@abstractmethod
def area(self):
"""计算面积(抽象方法,必须在子类中实现)"""
pass
@abstractmethod
def perimeter(self):
"""计算周长(抽象方法,必须在子类中实现)"""
pass
def describe(self):
"""描述形状(具体方法,子类可以继承)"""
return f"我是一个形状,面积:{self.area()}, 周长:{self.perimeter()}"
class Rectangle(Shape):
"""Rectangle类实现Shape抽象类"""
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
"""实现抽象方法area"""
return self.width * self.height
def perimeter(self):
"""实现抽象方法perimeter"""
return 2 * (self.width + self.height)
class Circle(Shape):
"""Circle类实现Shape抽象类"""
def __init__(self, radius):
self.radius = radius
def area(self):
"""实现抽象方法area"""
return 3.14159 * self.radius ** 2
def perimeter(self):
"""实现抽象方法perimeter"""
return 2 * 3.14159 * self.radius
# 创建Rectangle和Circle实例
rectangle = Rectangle(5, 10)
circle = Circle(7)
# 调用方法
print(rectangle.describe())
print(circle.describe())
# 尝试实例化抽象类(会引发TypeError)
try:
shape = Shape()
except TypeError as e:
print(f"错误:{e}")
9.2 接口
Python中没有正式的接口概念,但可以使用抽象类实现类似接口的功能:
from abc import ABC, abstractmethod
class Drawable(ABC):
"""Drawable接口"""
@abstractmethod
def draw(self):
"""绘制方法"""
pass
class Resizable(ABC):
"""Resizable接口"""
@abstractmethod
def resize(self, factor):
"""调整大小方法"""
pass
# 实现两个接口
class Square(Drawable, Resizable):
def __init__(self, side):
self.side = side
def draw(self):
print(f"绘制一个边长为{self.side}的正方形")
def resize(self, factor):
self.side *= factor
def area(self):
return self.side ** 2
# 创建Square实例
square = Square(5)
square.draw()
square.resize(2)
square.draw()
print(f"面积:{square.area()}")
10. 组合和聚合
10.1 组合
组合是一种强关联关系,表示"部分属于整体",整体销毁时,部分也会被销毁:
class Engine:
"""发动机类"""
def __init__(self, power):
self.power = power
def start(self):
print(f"启动{self.power}马力的发动机")
class Wheel:
"""车轮类"""
def __init__(self, size):
self.size = size
class Car:
"""汽车类(组合了Engine和Wheel)"""
def __init__(self, brand, model, engine_power, wheel_size):
self.brand = brand
self.model = model
self.engine = Engine(engine_power) # 组合:Car包含Engine
self.wheels = [Wheel(wheel_size) for _ in range(4)] # 组合:Car包含4个Wheel
def start(self):
print(f"启动{self.brand} {self.model}")
self.engine.start()
# 创建Car实例
car = Car("Toyota", "Camry", 180, 17)
car.start()
# 当car被销毁时,其包含的engine和wheels也会被销毁
10.2 聚合
聚合是一种弱关联关系,表示"整体包含部分",整体销毁时,部分不会被销毁:
class Author:
"""作者类"""
def __init__(self, name):
self.name = name
self.books = [] # 聚合:Author包含Book,但Book可以独立存在
def add_book(self, book):
self.books.append(book)
class Book:
"""书籍类"""
def __init__(self, title):
self.title = title
# 创建Author和Book实例
author = Author("张三")
book1 = Book("Python编程")
book2 = Book("Java编程")
# 聚合:将Book添加到Author
author.add_book(book1)
author.add_book(book2)
# 输出作者的书籍
print(f"{author.name}的著作:")
for book in author.books:
print(f"- {book.title}")
# 即使author被销毁,book1和book2仍然存在
11. 面向对象设计原则
11.1 单一职责原则(SRP)
一个类应该只有一个引起它变化的原因:
# 违反SRP的例子
class User:
def __init__(self, name, email):
self.name = name
self.email = email
def save(self):
"""保存用户到数据库"""
print(f"保存用户{self.name}到数据库")
def send_email(self, message):
"""发送邮件"""
print(f"给{self.email}发送邮件:{message}")
# 遵循SRP的例子
class User:
def __init__(self, name, email):
self.name = name
self.email = email
class UserRepository:
def save(self, user):
"""保存用户到数据库"""
print(f"保存用户{user.name}到数据库")
class EmailService:
def send_email(self, user, message):
"""发送邮件"""
print(f"给{user.email}发送邮件:{message}")
11.2 开放封闭原则(OCP)
软件实体(类、模块、函数等)应该对扩展开放,对修改封闭:
# 违反OCP的例子
class Shape:
def __init__(self, shape_type, *args):
self.shape_type = shape_type
if shape_type == "rectangle":
self.width, self.height = args
elif shape_type == "circle":
self.radius = args[0]
class AreaCalculator:
def calculate_area(self, shape):
if shape.shape_type == "rectangle":
return shape.width * shape.height
elif shape.shape_type == "circle":
return 3.14159 * shape.radius ** 2
# 添加新形状需要修改calculate_area方法
# 遵循OCP的例子
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self):
pass
class Rectangle(Shape):
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
return self.width * self.height
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
def area(self):
return 3.14159 * self.radius ** 2
# 添加新形状不需要修改AreaCalculator
class Triangle(Shape):
def __init__(self, base, height):
self.base = base
self.height = height
def area(self):
return 0.5 * self.base * self.height
class AreaCalculator:
def calculate_area(self, shape):
return shape.area() # 对扩展开放,对修改封闭
11.3 里氏替换原则(LSP)
子类应该能够替换父类而不改变程序的正确性:
# 违反LSP的例子
class Bird:
def fly(self):
print("可以飞行")
class Ostrich(Bird):
def fly(self):
raise NotImplementedError("鸵鸟不能飞行")
# 遵循LSP的例子
class Bird:
pass
class FlyingBird(Bird):
def fly(self):
print("可以飞行")
class NonFlyingBird(Bird):
pass
class Ostrich(NonFlyingBird):
pass
11.4 接口隔离原则(ISP)
客户端不应该依赖它不需要的接口:
# 违反ISP的例子
from abc import ABC, abstractmethod
class Worker(ABC):
@abstractmethod
def work(self):
pass
@abstractmethod
def eat(self):
pass
class Human(Worker):
def work(self):
print("人类工作")
def eat(self):
print("人类吃饭")
class Robot(Worker):
def work(self):
print("机器人工作")
def eat(self):
# 机器人不需要吃饭,但必须实现该方法
raise NotImplementedError("机器人不需要吃饭")
# 遵循ISP的例子
from abc import ABC, abstractmethod
class Workable(ABC):
@abstractmethod
def work(self):
pass
class Eatable(ABC):
@abstractmethod
def eat(self):
pass
class Human(Workable, Eatable):
def work(self):
print("人类工作")
def eat(self):
print("人类吃饭")
class Robot(Workable):
def work(self):
print("机器人工作")
# 机器人只实现它需要的接口
11.5 依赖倒置原则(DIP)
高层模块不应该依赖低层模块,两者都应该依赖抽象;抽象不应该依赖细节,细节应该依赖抽象:
# 违反DIP的例子
class MySQLDatabase:
def save(self, data):
print(f"保存数据到MySQL: {data}")
class UserService:
def __init__(self):
self.db = MySQLDatabase() # 依赖具体实现
def save_user(self, user):
self.db.save(user)
# 遵循DIP的例子
from abc import ABC, abstractmethod
class Database(ABC):
@abstractmethod
def save(self, data):
pass
class MySQLDatabase(Database):
def save(self, data):
print(f"保存数据到MySQL: {data}")
class MongoDB(Database):
def save(self, data):
print(f"保存数据到MongoDB: {data}")
class UserService:
def __init__(self, db: Database): # 依赖抽象接口
self.db = db
def save_user(self, user):
self.db.save(user)
# 使用依赖注入
mysql_db = MySQLDatabase()
user_service = UserService(mysql_db)
user_service.save_user({"name": "张三"})
mongo_db = MongoDB()
user_service = UserService(mongo_db)
user_service.save_user({"name": "李四"})
12. 最佳实践
12.1 类命名规范
- 类名使用大驼峰命名法(PascalCase)
- 方法和属性名使用小驼峰命名法或下划线命名法(推荐)
- 常量名使用全大写,单词间用下划线分隔
- 私有属性和方法以双下划线开头
class BankAccount:
MIN_BALANCE = 0 # 常量
def __init__(self, account_number):
self._account_number = account_number # 受保护属性
self.__balance = 0 # 私有属性
def deposit(self, amount):
"""存款"""
self.__balance += amount
def _validate_amount(self, amount):
"""验证金额(内部方法)"""
return amount > 0
12.2 类的设计
- 保持类的职责单一
- 类的大小适中,不要太大
- 优先使用组合而不是继承
- 合理使用继承和多态
- 避免深度继承(一般不超过3层)
12.3 属性和方法的设计
- 使用属性装饰器(@property)控制属性访问
- 方法名应该清晰地表达其功能
- 方法参数不宜过多(一般不超过5个)
- 为方法添加文档字符串
12.4 异常处理
- 合理使用异常处理
- 不要捕获所有异常
- 提供有意义的异常信息
12.5 测试
- 为类和方法编写单元测试
- 使用断言验证类的行为
- 测试边界条件
13. 总结
面向对象编程是Python编程的重要范式,它将数据和方法封装在一起,形成对象,并通过对象之间的交互来构建程序。本文详细介绍了Python面向对象编程的核心概念:
- 类和对象:类是对象的模板,对象是类的实例
- 属性和方法:实例属性、类属性、实例方法、类方法、静态方法
- 继承:单继承、多继承、方法重写
- 多态:不同对象以相同方式响应相同消息
- 封装:隐藏实现细节,提供公共接口
- 特殊方法:实现对象的特殊行为
- 抽象类和接口:定义规范和契约
- 组合和聚合:对象之间的关系
- 面向对象设计原则:SRP、OCP、LSP、ISP、DIP
- 最佳实践:命名规范、类设计、属性和方法设计、异常处理、测试
通过学习和掌握这些概念,可以编写出更加模块化、可维护、可扩展的Python程序。
发布网站:荣殿教程(zhangrongdian.com) 作者:张荣殿 发布日期:2026-01-19