Python面向对象编程基础:类与对象语法入门

月亮慢半拍
月亮慢半拍
发布时间:2026-11-13| 6人浏览过
原创

面向初学者的Python面向对象编程(OOP)基础教程。从数据与行为分离的痛点出发,详细讲解类与对象的核心定义、__init__构造函数、self的底层逻辑、实例属性与类属性的区别与避坑要点,配有完整代码与内联SVG图示。

面向对象编程(OOP)的核心在于将原本分散的数据变量与操作这些数据的函数紧密整合为互相关联的实体。掌握类与对象,本质上就是掌握如何使用 class 定义一套复用模具,并通过构造函数 __init__ 和 self 指针按需浇筑出具备独立状态的真实对象。
Python面向对象编程基础:类与对象语法入门
图 0:类作为通用抽象规范,通过实例化生成彼此独立的数据对象实体

一、为什么需要类与对象:从散落字典到封装实体

假设我们正在开发一个简易的学生成绩统计程序。在没有学习面向对象之前,通常的做法是借助字典来保存每个学生的数据,并编写一系列零散函数来处理它们:

# 传统过程式代码:数据与操作完全分离
student_a = {"name": "张三", "math": 88, "english": 92}
student_b = {"name": "李四", "math": 75, "english": 80}

def calculate_average(student_dict):
    return (student_dict["math"] + student_dict["english"]) / 2

def print_report(student_dict):
    avg = calculate_average(student_dict)
    print(f"{student_dict['name']} 的平均分是: {avg:.1f}")

print_report(student_a)
示例输出:
张三 的平均分是: 90.0

这种写法在脚本只有十几行时运转良好。但随着系统业务扩张,问题便迅速暴露出来:

  • 数据格式缺乏硬性约束:如果有团队成员不小心写成 student_c = {"username": "王五", "score_math": 90},传给 calculate_average 时就会在运行时直接抛出 KeyError。
  • 行为函数无明确归属:函数 calculate_average 游离在外部,任何字典都可以被传进去,数据与对数据的合法操作之间缺乏边界。
  • 状态变更高危:任何函数都能随意修改字典里的字段,程序体量一旦增大,排查某个异常字段在何处被篡改会变得极为痛苦。

面向对象编程通过封装(Encapsulation)解决了这个问题。它把相关的“属性”(数据变量)和属于这些数据的“方法”(函数)打包封装到一个名为类(Class)的独立盒子里。

Python面向对象编程基础:类与对象语法入门
图 1:过程式散落数据与面向对象类封装体系的结构与边界对比

二、核心语法:类的定义、__init__ 与 self 的本质

在 Python 中,声明一个类使用关键字 class,类名按照 PEP 8 命名规范通常采用大驼峰命名法(PascalCase),例如 Student、BankAccount。

1. 基本语法定义

下面是一个标准的简单类定义:

class Student:
    # 构造方法(初始化方法)
    def __init__(self, name, math, english):
        self.name = name          # 实例属性:学生姓名
        self.math = math          # 实例属性:数学成绩
        self.english = english    # 实例属性:英语成绩

    # 实例方法
    def get_average(self):
        return (self.math + self.english) / 2

    def print_report(self):
        avg = self.get_average()
        print(f"[报告] {self.name} 的综合均分为: {avg:.1f}")

# 实例化两个完全独立的对象
s1 = Student("张三", 88, 92)
s2 = Student("李四", 75, 80)

# 调用对象的方法
s1.print_report()
s2.print_report()
示例输出:
[报告] 张三 的综合均分为: 90.0
[报告] 李四 的综合均分为: 77.5

2. __init__ 方法到底在做什么?

__init__ 是 Python 中的一个双下划线特殊方法(Magic Method),在外部执行 Student(...) 实例化时由 Python 解释器自动触发执行。

它的职责不是“创造对象”,而是“初始化对象”。当解释器在内存中分配好一块属于该实例的空间后,会立刻把这个新鲜的空对象作为第一个参数传递给 __init__,在其中挂载初始数据(例如 self.name = name)。

注意事项:__init__ 方法强制要求不能有显式的非 None 返回值。不要在 __init__ 里书写 return 123 或 return self,否则会抛出 TypeError: __init__() should return None。

3. 彻底理解关键参数 self

初学者对面向对象最困惑的问题通常是:为什么定义方法时第一个参数总要写 self?为什么调用时明明传了三个参数却不用写 self?

在 Python 的底层机制中,实例方法本质上依然属于类。当执行 s1.print_report() 时,Python 解释器在内部执行的转换等价于:

# 以下两种调用方式在 Python 底层完全等价:
s1.print_report()              # 常用语法糖
Student.print_report(s1)       # 底层实际运行:明确把实例 s1 作为 self 传进类函数

因此,self 代表的正是“当前正在调用此方法的具体实例本身”。通过 self.name,方法内部就能精准访问或修改调用者自身存储的属性,而绝不会错误窜改其他实例的数据。

Python面向对象编程基础:类与对象语法入门
图 2:实例化完整五阶段执行流与 self 参数底层传递模型

三、实例属性 vs 类属性:避免初学者常见陷阱

在 Python 中,属性可以被定义在两个不同的级别:属于每一个独立对象的实例属性(Instance Attribute),以及由所有对象共享的类属性(Class Attribute)。理解两者的区别,是避免代码逻辑产生诡异 Bug 的关键。

1. 概念与定义位置的差异

  • 实例属性:通常在 __init__ 内通过 self.属性名 = 值 声明。它直接保存在该实例独享的 __dict__ 字典中。
  • 类属性:直接书写在 class 语句块内部、方法之外。它是所有由该类派生出的实例所共同享有的公用数据,存储在类本身的 __dict__ 中。
class Student:
    school = "第一实验中学"   # 类属性:所有学生共享同一个学校名

    def __init__(self, name):
        self.name = name           # 实例属性:每个学生名字各自独立

s1 = Student("张三")
s2 = Student("李四")

print(s1.school)  # 输出: 第一实验中学
print(s2.school)  # 输出: 第一实验中学

2. 致命陷阱:类属性中定义可变对象

当类属性是字符串、整数或元组这类不可变对象时,通常非常安全。但一旦在类属性中放置了列表(list)或字典(dict)等可变容器,就会引发极其隐蔽的共享污染 Bug:

# 【错误示范】:在类属性中放置可变列表
class BadStudent:
    courses = []  # 危险!所有实例共享同一个列表

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

    def add_course(self, course_name):
        self.courses.append(course_name)

b1 = BadStudent("张三")
b2 = BadStudent("李四")

b1.add_course("Python程序设计")
print(f"张三的课程: {b1.courses}")
print(f"李四的课程: {b2.courses}")  # 李四并没有选这门课!
示例输出(Bug 表现):
张三的课程: ['Python程序设计']
李四的课程: ['Python程序设计']

出现这种现象的原因在于:b1.courses.append(...) 并没有向 b1 自身的字典中写入任何新属性,而是首先在 b1 中寻找 courses;由于 b1 本身没有,Python 沿着查找链向上找到了类属性 BadStudent.courses,随后直接就地修改了全局共享的同一个列表。结果导致李四的数据也被连带污染。

3. 正确写法:在 __init__ 中声明实例独有属性

# 【正确示范】:在 __init__ 中为每个实例分配独享容器
class GoodStudent:
    def __init__(self, name):
        self.name = name
        self.courses = []  # 每个对象实例化时,独立创建专属的空列表

    def add_course(self, course_name):
        self.courses.append(course_name)

g1 = GoodStudent("张三")
g2 = GoodStudent("李四")

g1.add_course("Python程序设计")
print(f"张三的课程: {g1.courses}")
print(f"李四的课程: {g2.courses}")
示例输出(正确结果):
张三的课程: ['Python程序设计']
李四的课程: []
Python面向对象编程基础:类与对象语法入门
图 3:实例与类的命名空间层级结构与双向查找回溯链条

四、编写你的第一个完整业务类:BankAccount 实战

为了加深理解,我们来编写一个完整的业务场景类:模拟银行账户管理。这个类需要具备存款、取款、查询余额等功能,并包含基本的输入合法性校验。

class BankAccount:
    # 类属性:该银行所有账户的默认币种
    currency = "CNY"

    def __init__(self, owner, initial_balance=0.0):
        # 实例属性:开户人姓名与初始余额
        self.owner = owner
        if initial_balance < 0:
            raise ValueError("开户余额不可为负数")
        self.balance = float(initial_balance)
        self.transaction_history = []  # 每个账户独享的流水记录

    def deposit(self, amount):
        """存款操作"""
        if amount <= 0:
            print(f"[失败] {self.owner}: 存款金额必须大于 0")
            return False
        
        self.balance += amount
        self.transaction_history.append(f"存款: +{amount:.2f}")
        print(f"[成功] {self.owner} 存入 {amount:.2f} {self.currency},当前余额: {self.balance:.2f}")
        return True

    def withdraw(self, amount):
        """取款操作"""
        if amount <= 0:
            print(f"[失败] {self.owner}: 取款金额必须大于 0")
            return False
        if amount > self.balance:
            print(f"[拒绝] {self.owner}: 余额不足 (当前余额: {self.balance:.2f},尝试支取: {amount:.2f})")
            return False
            
        self.balance -= amount
        self.transaction_history.append(f"取款: -{amount:.2f}")
        print(f"[成功] {self.owner} 取出 {amount:.2f} {self.currency},剩余余额: {self.balance:.2f}")
        return True

    def print_statement(self):
        """打印对账单"""
        print(f"=== 账户对账单: {self.owner} ===")
        for item in self.transaction_history:
            print(f"  - {item}")
        print(f"账户当前总结余: {self.balance:.2f} {self.currency}
")

# --- 实际操作流程演示 ---
account_alice = BankAccount("Alice", 1000.0)
account_bob = BankAccount("Bob", 200.0)

# 账户 Alice 进行交易
account_alice.deposit(500.0)
account_alice.withdraw(300.0)
account_alice.withdraw(2000.0)  # 触发超额拒绝

# 打印双方对账单验证状态隔离
account_alice.print_statement()
account_bob.print_statement()
示例输出:
[成功] Alice 存入 500.00 CNY,当前余额: 1500.00
[成功] Alice 取出 300.00 CNY,剩余余额: 1200.00
[拒绝] Alice: 余额不足 (当前余额: 1200.00,尝试支取: 2000.00)
=== 账户对账单: Alice ===
  - 存款: +500.00
  - 取款: -300.00
账户当前总结余: 1200.00 CNY

=== 账户对账单: Bob ===
账户当前总结余: 200.00 CNY

常见初学者语法错误与排查清单

  1. 漏写方法的 self 参数:
    def get_balance():  # 错误!调用 obj.get_balance() 时将抛出 TypeError: get_balance() takes 0 positional arguments but 1 was given
        return self.balance
    排查依据:看到报参参数多出来 1 个时,99% 是因为定义实例方法时第一位忘记写 self。
  2. 在方法内部引用属性漏写 self:
    def deposit(self, amount):
        balance += amount  # 错误!抛出 UnboundLocalError: local variable 'balance' referenced before assignment
    排查依据:类中属性必须显式加上 self.balance,否则解释器会误将其作为局部变量查找。
  3. 拼写错误引发 AttributeError: 由于 Python 允许动态挂载属性,如果不小心把 self.balance 错敲成 self.balence,可能会创建出一个意外的新字段,而原字段未受改变。

五、总结与进阶学习路径

理解面向对象编程是写出大规模、高复用 Python 程序的基石。类提供结构与行为的图纸,而对象则是承载具体业务运行的独立实体。构造器 __init__ 负责奠定初始状态,self 负责维护每次调用的边界与指向。

Python面向对象编程基础:类与对象语法入门
图 4:面向对象语法认知与进阶成长路径全景脉络

核心要点速查

  • 类(Class):用 class 关键字声明,相当于制造对象的模具和规范,本身不存储具体的个体数据。
  • 对象(Object):由类实例化生成的独立个体(obj = MyClass()),在内存中占据独立的命名空间。
  • __init__ 方法:实例化触发的初始化构造器,负责为刚创建的空对象挂载属性,必须返回 None。
  • self 机制:实例调用自身方法时,Python 自动将对象自身句柄作为第一形参传给方法,是方法定位所属对象属性的唯一桥梁。
  • 属性作用域:实例属性属于单个对象独享;类属性由所有对象共享。切勿在类属性中放置列表或字典等可变容器。
相关专题:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。