楼主: peppa97
178 0

[其他] 【Python】基础语法入门(十六)——面向对象编程(OOP)核心精讲 [推广有奖]

  • 0关注
  • 0粉丝

等待验证会员

小学生

71%

还不是VIP/贵宾

-

威望
0
论坛币
0 个
通用积分
0
学术水平
0 点
热心指数
0 点
信用等级
0 点
经验
60 点
帖子
5
精华
0
在线时间
0 小时
注册时间
2018-3-20
最后登录
2018-3-20

楼主
peppa97 发表于 2025-12-12 11:25:08 |AI写论文

+2 论坛币
k人 参与回答

经管之家送您一份

应届毕业生专属福利!

求职就业群
赵安豆老师微信:zhaoandou666

经管之家联合CDA

送您一个全额奖学金名额~ !

感谢您参与论坛问题回答

经管之家送您两个论坛币!

+2 论坛币

虽然面向对象编程(OOP)在之前的章节中已有初步涉及,但由于其在软件开发中的核心地位,本文将对其进行一次系统的回顾与深化。重点聚焦于 OOP 的四大核心原则——封装、抽象、继承与多态,并结合 Python 语言的独特实现方式展开讲解。你将学会如何通过类(Class)对现实世界进行优雅建模,从而编写出结构清晰、易于维护和扩展的代码。

无论你是初次接触面向对象,还是希望巩固已有知识,本文都能帮助你:

  • 深入理解
    self
    的本质
  • 掌握属性与方法的合理设计方式
  • 灵活运用继承与多态机制
  • 识别并规避常见编程陷阱

1. 为什么需要面向对象?

过程式编程的局限性

假设我们用过程式的方式模拟两个学生信息:

name1 = "小明"
age1 = 18
grade1 = "高一"
name2 = "小红"
age2 = 17
grade2 = "高二"

def print_student(name, age, grade):
    print(f"{name}, {age}岁, {grade}")
    

存在的问题:数据与操作函数彼此分离,导致逻辑松散;当数据量增加时,参数传递容易出错(如顺序混乱),且难以维护和扩展。

面向对象的解决方案

使用类将数据和行为统一组织:

class Student:
    def __init__(self, name, age, grade):
        self.name = name
        self.age = age
        self.grade = grade
    
    def introduce(self):
        print(f"{self.name}, {self.age}岁, {grade}")

s1 = Student("小明", 18, "高一")
s2 = Student("小红", 17, "高二")
s1.introduce()
    

优势在于:将相关数据和操作封装在一起,结构清晰,便于复用与管理。

2. 类与对象:核心概念解析

概念 说明
类(Class) 作为创建对象的模板或蓝图(例如“学生”这一抽象概念)
对象(Object/Instance) 根据类创建的具体实例(例如“小明”这个具体的学生)
属性(Attribute) 对象所持有的数据,如姓名、年龄等
name
,
age
方法(Method) 对象可执行的行为或功能
introduce()

__init__
self

__init__
:构造方法,在创建对象时自动调用

self
:代表当前实例对象,必须作为实例方法的第一个参数

self
的本质:

当你调用

s1.introduce()
时,Python 实际上会将其转换为
Student.introduce(s1)
的形式来执行。

self
正是那个被隐式传入的
s1

3. 封装(Encapsulation):隐藏内部实现细节

通过限制外部对对象内部状态的直接访问,仅提供受控的接口进行交互,提升安全性和可维护性。

Python 中的“私有”约定机制

  • 单下划线 _attribute
    _x
    :表示“受保护”,是一种编程约定,提示开发者不应直接访问
  • 双下划线 __attribute
    __x
    :触发名称改写(name mangling),实现一定程度上的“隐藏”
class BankAccount:
    def __init__(self, owner, balance=0):
        self.owner = owner
        self.__balance = balance  # 私有属性

    def deposit(self, amount):
        if amount > 0:
            self.__balance += amount
        else:
            raise ValueError("存款金额必须为正")

    def get_balance(self):
        return self.__balance  # 提供安全访问途径

acc = BankAccount("Alice")
acc.deposit(100)
print(acc.get_balance())  # 输出: 100
# print(acc.__balance)    # 抛出 AttributeError!
print(acc._BankAccount__balance)  # 可技术访问,但强烈不推荐!
    

最佳实践建议:应优先使用

property
等机制提供更优雅、可控的属性访问方式(详见后续内容)。

4. 继承(Inheritance):实现代码复用

子类可以继承父类的属性和方法,并在此基础上进行功能扩展或行为重写,有效避免重复编码。

示例:学生与研究生的关系建模

class Student:
    def __init__(self, name, student_id):
        self.name = name
        self.student_id = student_id

    def study(self):
        print(f"{self.name} 正在学习")

class GraduateStudent(Student):  # 继承自 Student 类
    def __init__(self, name, student_id, advisor):
        super().__init__(name, student_id)  # 调用父类构造函数
        self.advisor = advisor

    def research(self):
        print(f"{self.name} 在 {self.advisor} 指导下做研究")

grad = GraduateStudent("李华", "G123", "张教授")
    

grad.study() # 继承自 Student
grad.research() # 自有方法

:支持多重继承,确保父类方法的安全调用。

5. 多态(Polymorphism):统一接口,多种实现

不同类的实例可以对同一个方法产生不同的响应行为。

示例:动物叫声

class Animal:
    def speak(self):
        raise NotImplementedError("子类必须实现 speak 方法")

class Dog(Animal):
    def speak(self):
        return "汪汪!"

class Cat(Animal):
    def speak(self):
        return "喵喵~"

def make_animal_speak(animal):
    print(animal.speak())  # 同一接口,触发不同实现

make_animal_speak(Dog())  # 输出:汪汪!
make_animal_speak(Cat())  # 输出:喵喵~
    
super()

优势:

  • 无需关注对象的具体类型
  • 只要实现了指定方法即可被统一处理
make_animal_speak
speak

6. 特殊方法(Magic Methods / Dunder Methods)

通过双下划线包围的方法,用于定制类在各种操作下的内置行为。

方法 作用 示例说明
__str__ 定义对象的可读字符串表示
__str__
__repr__ 提供开发者调试时使用的“官方”字符串形式
print(obj)
__len__ 使对象支持 len(obj) 调用
return f"学生: {self.name}"
__getitem__ 支持索引访问 obj[key]
__repr__
__setitem__ 支持赋值操作 obj[key] = value
return f"Student('{self.name}')"
__iter__ 使对象可迭代,用于 for 循环
__len__
__contains__ 支持 in 操作符判断成员关系
len(obj)
__eq__, __lt__ 等 支持对象之间的比较运算
return len(self.items)
__add__, __mul__ 等 支持自定义类型的算术运算
__eq__
==
return self.id == other.id
__add__
+
return Vector(self.x+other.x, ...)

示例:让自定义类支持 len() 和 str()

class Bookshelf:
    def __init__(self, books):
        self.books = books

    def __len__(self):
        return len(self.books)

    def __str__(self):
        return f"书架上有 {len(self)} 本书"

    def __repr__(self):
        return f"Bookshelf({self.books!r})"

shelf = Bookshelf(["Python入门", "算法导论"])
print(len(shelf))   # 输出:2
print(shelf)        # 输出:书架上有 2 本书
    
len()
print()

黄金法则:

  • __str__ 面向用户,注重友好性与可读性
  • __repr__ 面向开发者,应尽量明确且可还原对象状态(理想情况下 eval(repr(obj)) 可重建对象)
__str__
__repr__
eval(repr(obj)) == obj

7. 属性装饰器(@property):将方法伪装成属性

使得 getter 和 setter 的使用更加自然,同时维持封装性。

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("半径不能为负")
        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,自动进行合法性校验
    

优势:

  • 外部调用者无需区分该值是直接存储还是通过计算获得
  • 可以在不改变接口的前提下添加数据校验逻辑
@property
radius

8. 常见误区与最佳实践

常见误区 正确做法
滥用继承(如“万物皆可继承”) 优先使用组合(Composition),更灵活、低耦合
忽略 super() 在多重继承中的作用 多重继承中必须正确调用 super() 保证方法解析顺序(MRO)正常执行
将所有属性设为公开 使用 @property 或命名约定(如 _ 开头)控制访问权限
缺少文档字符串 为类和关键方法添加清晰的文档说明
过度设计复杂继承结构 从简单类开始,根据实际需求逐步重构优化
__init__
super()
property
"""说明"""

组合优于继承示例

不推荐:错误的继承使用

class Bird:
    def fly(self): pass

class Penguin(Bird):  # 问题:企鹅不会飞!
    def fly(self):
        raise NotImplementedError
    

推荐:使用组合方式

class Flyer:
    def fly(self):
        print("Flying!")

class Bird:
    def __init__(self, can_fly=True):
        self.flyer = Flyer() if can_fly else None

    def fly(self):
        if self.flyer:
            self.flyer.fly()
        else:
            print("我不会飞 ????")
    

9. 总结:OOP 设计原则

  • 封装:隐藏内部细节,暴露安全接口
  • 继承:代码复用的有效手段,但不宜滥用
  • 多态:提升程序扩展性,实现接口统一化处理
  • 组合优于继承:提高灵活性,降低耦合度
  • 善用特殊方法:增强类的自然性和可用性
  • 合理使用 @property:平衡简洁语法与数据控制

当你面对的问题适合通过对象来建模时,才应当使用面向对象编程(OOP)。记住:OOP 是一种工具,而不是最终目的。

在设计类结构时,可以参考以下几项核心原则:

  • 单一职责:每个类应只承担一个职责,专注于完成一件事。
  • 开闭原则:软件实体应对扩展开放,对修改关闭。
  • 里氏替换:子类对象能够替换其父类对象而不影响程序的正确性。
  • 依赖倒置:高层模块不应依赖低层模块,二者都应依赖于抽象;抽象不应依赖细节,细节应依赖抽象。

接下来,尝试为你的 To-Do List 项目设计合理的类结构:

定义一个表示任务的类,包含标题、状态和创建时间等属性,并支持将某些字段设为只读属性,以确保数据的一致性与安全性。

Task

再定义一个用于管理任务列表的类,负责实现任务的增删查改操作,保持业务逻辑清晰且易于维护。

TaskManager

为任务管理功能添加必要的行为支持:

Task

引入

__str__

__repr__

并通过

@property

来实现

is_completed

当你能用类清晰地表达业务逻辑时,你就真正掌握了 Python 的面向对象之力。继续构建属于你的对象世界吧!

二维码

扫码加我 拉你入群

请注明:姓名-公司-职位

以便审核进群资格,未注明则拒绝

关键词:python 面向对象 composition Introduce attribute

您需要登录后才可以回帖 登录 | 我要注册

本版微信群
加好友,备注cda
拉您进交流群
GMT+8, 2025-12-21 06:20