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装饰器定义,没有selfcls参数,与类和实例无关:

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面向对象编程的核心概念:

  1. 类和对象:类是对象的模板,对象是类的实例
  2. 属性和方法:实例属性、类属性、实例方法、类方法、静态方法
  3. 继承:单继承、多继承、方法重写
  4. 多态:不同对象以相同方式响应相同消息
  5. 封装:隐藏实现细节,提供公共接口
  6. 特殊方法:实现对象的特殊行为
  7. 抽象类和接口:定义规范和契约
  8. 组合和聚合:对象之间的关系
  9. 面向对象设计原则:SRP、OCP、LSP、ISP、DIP
  10. 最佳实践:命名规范、类设计、属性和方法设计、异常处理、测试

通过学习和掌握这些概念,可以编写出更加模块化、可维护、可扩展的Python程序。


发布网站:荣殿教程(zhangrongdian.com) 作者:张荣殿 发布日期:2026-01-19