返回文章列表

Python面向对象编程指南

面向对象编程(Object-Oriented Programming,OOP)是Python最核心的编程范式之一。本文将系统讲解Python中类与对象的概念、继承与多态、封装与私有属性、特殊方法以及类方法与静态方法,最后通过一个图书管理系统实战案例巩固所学知识。

一、类与对象的基本概念

类(Class)是对象的蓝图或模板,定义了一类事物共有的属性和行为。对象(Object)是类的具体实例。可以理解为:类是"汽车"的设计图,对象是根据设计图造出来的具体某辆车。

# 定义一个简单的类
class Dog:
    """这是一个简单的Dog类"""

    # 类属性(所有实例共享)
    species = "犬科动物"

    def __init__(self, name, age):
        """构造函数,初始化实例属性"""
        self.name = name  # 实例属性
        self.age = age

    def bark(self):
        """实例方法"""
        return f"{self.name}说:汪汪汪!"

    def info(self):
        """返回狗狗的信息"""
        return f"{self.name},{self.age}岁,{self.species}"

# 创建对象(实例化)
dog1 = Dog("旺财", 3)
dog2 = Dog("小黑", 5)

print(dog1.bark())       # 旺财说:汪汪汪!
print(dog2.info())       # 小黑,5岁,犬科动物
print(Dog.species)       # 犬科动物(通过类名访问类属性)

二、self参数详解

self代表类的当前实例。在Python中定义实例方法时,第一个参数必须是self(虽然你可以用其他名字,但强烈建议使用self)。当调用方法时,Python会自动将当前实例作为self传入。

class Circle:
    def __init__(self, radius):
        self.radius = radius  # self.radius 是实例属性

    def area(self):
        """计算面积"""
        return 3.14159 * self.radius ** 2

    def circumference(self):
        """计算周长"""
        return 2 * 3.14159 * self.radius

    def compare(self, other):
        """比较两个圆的大小"""
        if self.radius > other.radius:
            return f"{self.radius}的圆比{other.radius}的圆大"
        elif self.radius < other.radius:
            return f"{self.radius}的圆比{other.radius}的圆小"
        else:
            return "两个圆一样大"

c1 = Circle(5)
c2 = Circle(10)

print(f"面积: {c1.area():.2f}")
print(f"周长: {c2.circumference():.2f}")
print(c1.compare(c2))

三、构造函数 __init__

__init__ 是Python中的构造方法(严格来说是初始化方法),在创建对象时自动调用。它用于初始化对象的属性。

class Student:
    school = "Python大学"  # 类属性

    def __init__(self, name, age, grade="大一"):
        self.name = name
        self.age = age
        self.grade = grade
        self.scores = []  # 默认可变属性使用列表,每个实例独立

    def add_score(self, score):
        """添加成绩"""
        if 0 <= score <= 100:
            self.scores.append(score)
            return True
        return False

    def average_score(self):
        """计算平均分"""
        if not self.scores:
            return 0
        return sum(self.scores) / len(self.scores)

    def __str__(self):
        return f"学生:{self.name},年龄:{self.age},年级:{self.grade}"

# 使用关键字参数
stu1 = Student("张三", 20)
stu2 = Student("李四", 21, grade="大二")

stu1.add_score(95)
stu1.add_score(88)
stu1.add_score(76)

print(stu1)
print(f"{stu1.name}的平均分:{stu1.average_score():.1f}")

四、继承与多态

继承允许子类(派生类)继承父类(基类)的属性和方法,实现代码复用。多态是指不同对象对同一方法的不同实现。

# 父类
class Animal:
    def __init__(self, name, sound):
        self.name = name
        self.sound = sound

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

    def move(self):
        return f"{self.name}正在移动"

# 子类继承父类
class Cat(Animal):
    def __init__(self, name, color):
        super().__init__(name, "喵喵喵")  # 调用父类构造函数
        self.color = color

    def climb(self):
        """子类特有的方法"""
        return f"{self.name}正在爬树"

class Bird(Animal):
    def __init__(self, name, wingspan):
        super().__init__(name, "叽叽喳喳")
        self.wingspan = wingspan

    # 方法重写(覆盖父类方法)
    def move(self):
        return f"{self.name}正在用{self.wingspan}厘米的翅膀飞翔"

# 多态演示
def animal_action(animal):
    print(animal.speak())
    print(animal.move())
    print("---")

animals = [
    Animal("未知动物", "???"),
    Cat("小花", "橘色"),
    Bird("小燕子", 15)
]

for animal in animals:
    animal_action(animal)

五、方法重写与super()函数

子类可以重写父类的方法来提供特定的实现。super()函数用于在子类中调用父类的方法,避免重复代码。

class Employee:
    def __init__(self, name, salary):
        self.name = name
        self.salary = salary

    def get_info(self):
        return f"员工:{self.name},基本工资:{self.salary}元"

    def yearly_bonus(self):
        return self.salary * 1  # 默认一个月奖金

class Manager(Employee):
    def __init__(self, name, salary, department, team_size):
        super().__init__(name, salary)
        self.department = department
        self.team_size = team_size

    def get_info(self):
        """重写父类方法,扩展信息"""
        base_info = super().get_info()
        return f"{base_info},部门:{self.department},团队人数:{self.team_size}"

    def yearly_bonus(self):
        """重写奖金计算方法"""
        base_bonus = super().yearly_bonus()
        team_bonus = self.team_size * 500  # 每人500元团队奖金
        return base_bonus + team_bonus

class Developer(Employee):
    def __init__(self, name, salary, language):
        super().__init__(name, salary)
        self.language = language

    def get_info(self):
        base_info = super().get_info()
        return f"{base_info},编程语言:{self.language}"

    def yearly_bonus(self):
        base_bonus = super().yearly_bonus()
        return base_bonus * 1.5  # 开发者奖金1.5倍

# 测试
emp = Employee("张三", 8000)
mgr = Manager("李四", 15000, "技术部", 8)
dev = Developer("王五", 12000, "Python")

print(emp.get_info())
print(f"  年终奖:{emp.yearly_bonus()}元")
print(mgr.get_info())
print(f"  年终奖:{mgr.yearly_bonus()}元")
print(dev.get_info())
print(f"  年终奖:{dev.yearly_bonus()}元")

六、封装与私有属性

封装是将数据(属性)和操作数据的方法绑定在一起,并隐藏内部实现细节。Python中使用下划线约定来实现访问控制:单下划线 _ 表示"受保护",双下划线 __ 表示"私有"。

class BankAccount:
    def __init__(self, owner, balance=0):
        self.owner = owner           # 公有属性
        self._balance = balance      # 受保护属性(约定不要直接访问)
        self.__transaction_log = []  # 私有属性(名称修饰)

    def deposit(self, amount):
        """存款"""
        if amount > 0:
            self._balance += amount
            self.__add_log(f"存入 {amount}元")
            return True
        print("存款金额必须大于0")
        return False

    def withdraw(self, amount):
        """取款"""
        if amount <= 0:
            print("取款金额必须大于0")
            return False
        if amount > self._balance:
            print("余额不足")
            return False
        self._balance -= amount
        self.__add_log(f"取出 {amount}元")
        return True

    def get_balance(self):
        """获取余额(通过方法访问受保护属性)"""
        return self._balance

    def __add_log(self, message):
        """私有方法:记录交易日志"""
        import datetime
        time_str = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        self.__transaction_log.append(f"[{time_str}] {message}")

    def show_log(self):
        """显示交易日志"""
        for log in self.__transaction_log:
            print(log)

    @property
    def balance(self):
        """用property装饰器提供对私有属性的访问"""
        return self._balance

    @balance.setter
    def balance(self, value):
        """限制余额不能为负数"""
        if value < 0:
            raise ValueError("余额不能为负数")
        self._balance = value

# 使用
account = BankAccount("平平", 1000)
account.deposit(500)
account.withdraw(200)
print(f"当前余额:{account.get_balance()}元")
print("--- 交易记录 ---")
account.show_log()

# property 的用法
print(f"\n通过property访问余额:{account.balance}元")
account.balance = 2000  # 通过setter修改
print(f"修改后余额:{account.balance}元")

七、特殊方法(魔术方法)

Python通过特殊方法(以双下划线开头和结尾)让自定义类的行为像内置类型一样自然。

class Vector:
    """二维向量类"""

    def __init__(self, x=0, y=0):
        self.x = x
        self.y = y

    # 字符串表示
    def __str__(self):
        return f"Vector({self.x}, {self.y})"

    def __repr__(self):
        return f"Vector({self.x}, {self.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 __mul__(self, scalar):
        if isinstance(scalar, (int, float)):
            return Vector(self.x * scalar, self.y * scalar)
        return NotImplemented

    # 反向乘法(支持 scalar * vector)
    def __rmul__(self, scalar):
        return self.__mul__(scalar)

    # 长度
    def __abs__(self):
        return (self.x ** 2 + self.y ** 2) ** 0.5

    # 相等判断
    def __eq__(self, other):
        if isinstance(other, Vector):
            return self.x == other.x and self.y == other.y
        return NotImplemented

    # 布尔值判断(零向量为False)
    def __bool__(self):
        return self.x != 0 or self.y != 0

    # 长度(支持len())
    def __len__(self):
        return 2

    # 迭器支持
    def __iter__(self):
        return iter((self.x, self.y))

    # 下标访问
    def __getitem__(self, index):
        if index == 0:
            return self.x
        elif index == 1:
            return self.y
        raise IndexError("向量只有两个分量")

    # 小于比较
    def __lt__(self, other):
        return abs(self) < abs(other)

# 使用
v1 = Vector(3, 4)
v2 = Vector(1, 2)

print(f"v1 = {v1}")
print(f"v2 = {v2}")
print(f"v1 + v2 = {v1 + v2}")
print(f"v1 - v2 = {v1 - v2}")
print(f"v1 * 3 = {v1 * 3}")
print(f"|v1| = {abs(v1)}")
print(f"v1 == v2: {v1 == v2}")
print(f"bool(v1): {bool(v1)}")
print(f"v1[0] = {v1[0]}, v1[1] = {v1[1]}")
print(f"v1 < v2: {v1 < v2}")

八、类方法与静态方法

实例方法操作实例数据,类方法(@classmethod)操作类级别数据,静态方法(@staticmethod)是与类相关但不操作类或实例数据的工具函数。

import math

class Temperature:
    """温度转换工具类"""

    def __init__(self, celsius):
        self.celsius = celsius

    @property
    def fahrenheit(self):
        return self.celsius * 9 / 5 + 32

    @property
    def kelvin(self):
        return self.celsius + 273.15

    def __str__(self):
        return (f"{self.celsius}C = {self.fahrenheit:.1f}F "
                f"= {self.kelvin:.2f}K")

    @classmethod
    def from_fahrenheit(cls, f):
        """类方法:从华氏度创建实例"""
        celsius = (f - 32) * 5 / 9
        return cls(celsius)

    @classmethod
    def from_kelvin(cls, k):
        """类方法:从开尔文创建实例"""
        celsius = k - 273.15
        return cls(celsius)

    @staticmethod
    def is_valid_celsius(value):
        """静态方法:验证摄氏度值是否合理"""
        return -273.15 <= value <= 1_000_000

    @staticmethod
    def celsius_to_fahrenheit(c):
        """静态方法:简单的单位转换,不依赖实例"""
        return c * 9 / 5 + 32

# 使用实例方法
t1 = Temperature(100)
print(t1)  # 100C = 212.0F = 373.15K

# 使用类方法创建实例
t2 = Temperature.from_fahrenheit(98.6)
print(f"体温:{t2:.1f}C")

t3 = Temperature.from_kelvin(0)
print(f"绝对零度:{t3:.2f}C")

# 使用静态方法
print(f"100C有效吗?{Temperature.is_valid_celsius(100)}")
print(f"-300C有效吗?{Temperature.is_valid_celsius(-300)}")
print(f"0C转F:{Temperature.celsius_to_fahrenheit(0)}F")

九、实战案例:图书管理系统

综合运用以上知识,我们来实现一个功能完整的图书管理系统。

from datetime import datetime, timedelta

# ========== 基类:物品 ==========
class LibraryItem:
    """图书馆物品基类"""

    def __init__(self, title, item_id):
        self.title = title
        self.item_id = item_id
        self.is_borrowed = False
        self.borrower = None
        self.borrow_date = None

    def borrow(self, borrower_name):
        """借出"""
        if self.is_borrowed:
            print(f"《{self.title}》已被 {self.borrower} 借走")
            return False
        self.is_borrowed = True
        self.borrower = borrower_name
        self.borrow_date = datetime.now()
        print(f"{borrower_name} 成功借阅《{self.title}》")
        return True

    def return_item(self):
        """归还"""
        if not self.is_borrowed:
            print(f"《{self.title}》未借出")
            return False
        days = (datetime.now() - self.borrow_date).days
        self.is_borrowed = False
        print(f"《{self.title}》已归还(借阅 {days} 天)")
        self.borrower = None
        self.borrow_date = None
        return True

    def __str__(self):
        status = "已借出" if self.is_borrowed else "可借阅"
        return f"[{self.item_id}] 《{self.title}》 - {status}"


# ========== 子类:图书 ==========
class Book(LibraryItem):
    """图书类"""

    def __init__(self, title, author, isbn, pages):
        super().__init__(title, f"B-{isbn}")
        self.author = author
        self.isbn = isbn
        self.pages = pages
        self.category = "未分类"

    def info(self):
        return (f"《{self.title}》 - {self.author}\n"
                f"  ISBN: {self.isbn} | {self.pages}页 | 分类: {self.category}")


# ========== 子类:杂志 ==========
class Magazine(LibraryItem):
    """杂志类"""

    def __init__(self, title, issue, publisher):
        super().__init__(title, f"M-{issue}")
        self.issue = issue
        self.publisher = publisher

    def info(self):
        return f"《{self.title}》 第{self.issue}期 | {self.publisher}"


# ========== 图书馆管理类 ==========
class Library:
    """图书馆管理系统"""

    def __init__(self, name):
        self.name = name
        self._items = {}  # item_id -> LibraryItem

    def add_item(self, item):
        """添加物品"""
        self._items[item.item_id] = item
        print(f"已添加:{item}")

    def remove_item(self, item_id):
        """移除物品"""
        if item_id in self._items:
            item = self._items.pop(item_id)
            print(f"已移除:《{item.title}》")
            return True
        print(f"未找到ID为 {item_id} 的物品")
        return False

    def find_by_title(self, keyword):
        """按标题搜索"""
        results = []
        for item in self._items.values():
            if keyword in item.title:
                results.append(item)
        return results

    def find_available(self):
        """查找所有可借阅物品"""
        return [item for item in self._items.values()
                if not item.is_borrowed]

    def show_all(self):
        """显示所有物品"""
        print(f"\n{'='*50}")
        print(f"  {self.name} - 共 {len(self._items)} 件物品")
        print(f"{'='*50}")
        for item in self._items.values():
            print(f"  {item}")
        print()

    def show_borrowed(self):
        """显示借出列表"""
        borrowed = [i for i in self._items.values() if i.is_borrowed]
        print(f"\n--- 当前借出 ({len(borrowed)} 件) ---")
        for item in borrowed:
            print(f"  {item.item_id}: 《{item.title}》 - {item.borrower}")
        if not borrowed:
            print("  暂无借出")
        print()


# ========== 使用示例 ==========
if __name__ == "__main__":
    lib = Library("平平的小图书馆")

    # 添加图书
    b1 = Book("Python编程:从入门到实践", "Eric Matthes", "9787115428028", 459)
    b1.category = "编程"
    b2 = Book("流畅的Python", "Luciano Ramalho", "9787115454157", 688)
    b2.category = "编程"
    b3 = Book("百年孤独", "加西亚·马尔克斯", "9787544253994", 360)
    b3.category = "文学"

    # 添加杂志
    m1 = Magazine("Python Weekly", "2024-01", "Python Software Foundation")
    m2 = Magazine("程序员", "2024-03", "电子工业出版社")

    for item in [b1, b2, b3, m1, m2]:
        lib.add_item(item)

    # 显示所有
    lib.show_all()

    # 借书
    b1.borrow("张三")
    b3.borrow("李四")

    # 再次借已被借走的书
    b1.borrow("王五")

    # 归还
    b3.return_item()

    # 搜索
    print("\n--- 搜索'Python' ---")
    for item in lib.find_by_title("Python"):
        print(f"  {item.info()}")

    print("\n--- 可借阅列表 ---")
    for item in lib.find_available():
        print(f"  {item}")

    lib.show_borrowed()

以上就是Python面向对象编程的完整指南。从类与对象的基本概念,到继承与多态、封装与私有属性、特殊方法、类方法与静态方法,再到图书管理系统的实战案例。掌握这些知识后,你就能够用面向对象的方式组织复杂程序的代码结构,提高代码的可维护性和可复用性。

十、总结与要点回顾

动手挑战

学到这里,不妨动手试一试以下练习,巩固你的理解:

  1. 基础练习:回顾本文核心概念,用自己的话总结关键知识点。
  2. 进阶实践:将文中的示例代码运行一遍,尝试修改参数观察变化。
  3. 拓展思考:想一想这个技术/方法还能应用在哪些场景中?

小贴士:遇到问题时,先独立思考,再查阅资料,最后请教他人——这是成长最快的学习方式。

赞赏支持

本文更新于 2026-08-22,环境 Python 3.12