Python教程31:类与对象基础

“万物皆对象。”

从今天开始,我们进入Python编程的新境界——面向对象编程(OOP)。这是一种组织代码的强大范式,让程序更贴近真实世界的思维方式。

1. 什么是面向对象编程

编程范式的演进

面向过程编程(Procedural Programming)

  • 以函数为中心
  • 数据和操作分离
  • 适合简单问题
 1# 面向过程:管理学生信息
 2students = [
 3    {"name": "Alice", "age": 20, "grade": 85},
 4    {"name": "Bob", "age": 21, "grade": 90}
 5]
 6
 7def calculate_average(students):
 8    """计算平均成绩"""
 9    total = sum(s["grade"] for s in students)
10    return total / len(students)
11
12# 数据和操作分离

面向对象编程(Object-Oriented Programming, OOP)

  • 以对象为中心
  • 数据和操作封装在一起
  • 更贴近真实世界建模
 1# 面向对象:学生类
 2class Student:
 3    """学生类:数据和行为封装在一起"""
 4    def __init__(self, name, age, grade):
 5        self.name = name
 6        self.age = age
 7        self.grade = grade
 8    
 9    def get_info(self):
10        """学生的行为"""
11        return f"{self.name}, {self.age}岁, 成绩{self.grade}"
12
13# 创建对象
14alice = Student("Alice", 20, 85)
15bob = Student("Bob", 21, 90)

OOP的核心概念

类(Class)

  • 对象的模板/蓝图
  • 定义了对象的属性和方法
  • 像是"饼干模具"

对象(Object)

  • 类的实例
  • 具有实际的数据
  • 像是用模具做出的"饼干"

为什么需要OOP

  • 封装:数据和操作组织在一起
  • 抽象:隐藏复杂细节,暴露简单接口
  • 复用:通过继承重用代码
  • 可维护:修改更容易,影响范围更小
  • 真实世界建模:程序结构更接近问题域

2. 定义类

基本语法

1class ClassName:
2    """类的文档字符串"""
3    
4    # 类体
5    pass

第一个类

 1class Dog:
 2    """
 3    狗类
 4    - 定义狗的属性和行为
 5    - 这是一个简单的示例类
 6    """
 7    
 8    # 类属性(所有实例共享)
 9    species = "Canis familiaris"  # 物种
10    
11    def __init__(self, name, age):
12        """
13        初始化方法(构造函数)
14        - self代表实例本身
15        - name和age是实例属性
16        
17        Args:
18            name: 狗的名字
19            age: 狗的年龄
20        """
21        self.name = name  # 实例属性
22        self.age = age
23    
24    def bark(self):
25        """
26        狗叫的方法
27        - 实例方法的第一个参数必须是self
28        """
29        return f"{self.name} says: Woof!"
30    
31    def get_info(self):
32        """获取狗的信息"""
33        return f"{self.name} is {self.age} years old"
34
35# 创建对象(实例化)
36buddy = Dog("Buddy", 3)
37print(buddy.name)      # Buddy
38print(buddy.bark())    # Buddy says: Woof!
39print(buddy.get_info())  # Buddy is 3 years old

关键概念

__init__方法

  • 特殊方法(魔术方法),双下划线开头和结尾
  • 类的初始化方法(构造函数)
  • 创建对象时自动调用
  • 用于设置对象的初始状态

self参数

  • 代表实例本身
  • 必须是实例方法的第一个参数
  • 调用时Python自动传递,不需要手动传
  • 可以用其他名字,但约定俗成用self
 1# self的作用
 2class Example:
 3    def __init__(self, value):
 4        self.value = value  # self.value是实例属性
 5    
 6    def show(self):
 7        print(self.value)  # 通过self访问实例属性
 8
 9obj = Example(10)
10obj.show()  # 10
11# 实际上调用:Example.show(obj)

3. 类属性 vs 实例属性

类属性

所有实例共享的属性:

 1class Circle:
 2    """圆类"""
 3    # 类属性:所有圆共享同一个π值
 4    pi = 3.14159
 5    count = 0  # 计数器
 6    
 7    def __init__(self, radius):
 8        self.radius = radius  # 实例属性:每个圆有自己的半径
 9        Circle.count += 1  # 修改类属性
10    
11    def area(self):
12        """计算面积"""
13        return Circle.pi * self.radius ** 2
14
15# 使用
16c1 = Circle(5)
17c2 = Circle(10)
18
19# 访问类属性
20print(Circle.pi)     # 3.14159
21print(c1.pi)         # 3.14159(也可以通过实例访问)
22print(Circle.count)  # 2(创建了2个实例)

实例属性

每个实例独有的属性:

 1class Person:
 2    """人类"""
 3    def __init__(self, name, age):
 4        # 实例属性:每个人有自己的名字和年龄
 5        self.name = name
 6        self.age = age
 7
 8p1 = Person("Alice", 25)
 9p2 = Person("Bob", 30)
10
11print(p1.name)  # Alice
12print(p2.name)  # Bob(不同的实例有不同的属性值)

类属性 vs 实例属性对比

 1class Example:
 2    class_var = "类属性"
 3    
 4    def __init__(self):
 5        self.instance_var = "实例属性"
 6
 7# 类属性:通过类访问
 8print(Example.class_var)  # 类属性
 9
10# 实例属性:必须通过实例访问
11obj = Example()
12print(obj.instance_var)   # 实例属性
13print(obj.class_var)      # 也可以访问类属性
14
15# 修改类属性
16Example.class_var = "新值"
17print(obj.class_var)      # 新值(所有实例都受影响)
18
19# 实例属性遮蔽类属性
20obj.class_var = "实例的值"
21print(obj.class_var)      # 实例的值(只影响这个实例)
22print(Example.class_var)  # 新值(类属性未变)

4. 方法

实例方法

最常见的方法类型:

 1class BankAccount:
 2    """银行账户类"""
 3    
 4    def __init__(self, owner, balance=0):
 5        """初始化账户"""
 6        self.owner = owner
 7        self.balance = balance
 8    
 9    def deposit(self, amount):
10        """
11        存款(实例方法)
12        - 第一个参数是self
13        - 可以访问和修改实例属性
14        """
15        if amount > 0:
16            self.balance += amount
17            return f"存入{amount}元,余额{self.balance}元"
18        return "金额必须大于0"
19    
20    def withdraw(self, amount):
21        """取款"""
22        if amount > self.balance:
23            return "余额不足"
24        self.balance -= amount
25        return f"取出{amount}元,余额{self.balance}元"
26    
27    def get_balance(self):
28        """查询余额"""
29        return f"{self.owner}的余额:{self.balance}元"
30
31# 使用
32account = BankAccount("张三", 1000)
33print(account.deposit(500))    # 存入500元,余额1500元
34print(account.withdraw(200))   # 取出200元,余额1300元
35print(account.get_balance())   # 张三的余额:1300元

方法的本质

方法是绑定到实例的函数:

 1class Example:
 2    def method(self, x):
 3        return self.value + x
 4
 5obj = Example()
 6obj.value = 10
 7
 8# 两种调用方式
 9print(obj.method(5))           # 15(常用)
10print(Example.method(obj, 5))  # 15(揭示本质)

5. 封装与私有属性

公开属性

默认情况下,所有属性和方法都是公开的:

1class Person:
2    def __init__(self, name):
3        self.name = name  # 公开属性
4
5p = Person("Alice")
6print(p.name)    # 直接访问
7p.name = "Bob"   # 直接修改

私有属性(名称改编)

Python使用__前缀表示私有:

 1class BankAccount:
 2    """银行账户"""
 3    
 4    def __init__(self, owner, balance):
 5        self.owner = owner
 6        self.__balance = balance  # 私有属性(双下划线开头)
 7    
 8    def deposit(self, amount):
 9        """存款"""
10        if amount > 0:
11            self.__balance += amount
12    
13    def get_balance(self):
14        """获取余额(提供公开接口)"""
15        return self.__balance
16
17account = BankAccount("张三", 1000)
18
19# 不能直接访问私有属性
20# print(account.__balance)  # AttributeError
21
22# 通过公开方法访问
23print(account.get_balance())  # 1000
24
25# 实际上Python做了名称改编(name mangling)
26print(account._BankAccount__balance)  # 1000(不推荐这样访问)

Python的私有性

  • Python没有真正的私有
  • __前缀触发名称改编
  • 实际变成_ClassName__attribute
  • 这是约定,不是强制

受保护属性(单下划线)

1class Example:
2    def __init__(self):
3        self._protected = "受保护"  # 单下划线:约定为内部使用
4        self.__private = "私有"     # 双下划线:名称改编
5
6obj = Example()
7print(obj._protected)  # 可以访问,但不推荐(约定)

命名约定

  • 无下划线:公开
  • 单下划线_:内部使用(约定)
  • 双下划线__:私有(名称改编)
  • 双下划线包围__name__:魔术方法

6. 对象的创建和销毁

init:初始化

 1class Example:
 2    def __init__(self, value):
 3        """
 4        初始化方法
 5        - 对象创建后立即调用
 6        - 设置对象的初始状态
 7        """
 8        print("对象正在初始化")
 9        self.value = value
10
11obj = Example(10)
12# 输出:对象正在初始化

del:析构

 1class Example:
 2    def __del__(self):
 3        """
 4        析构方法
 5        - 对象被垃圾回收前调用
 6        - 用于清理资源(文件、网络连接等)
 7        - 不要依赖它的执行时机
 8        """
 9        print("对象正在被销毁")
10
11obj = Example()
12del obj  # 手动删除
13# 输出:对象正在被销毁

7. 实用示例

学生管理系统

 1class Student:
 2    """学生类"""
 3    
 4    # 类属性:学生总数
 5    total_count = 0
 6    
 7    def __init__(self, name, student_id, grade):
 8        """初始化学生"""
 9        self.name = name
10        self.student_id = student_id
11        self.grade = grade
12        self.courses = []  # 课程列表
13        
14        Student.total_count += 1
15    
16    def enroll_course(self, course):
17        """选课"""
18        if course not in self.courses:
19            self.courses.append(course)
20            return f"{self.name}已选课:{course}"
21        return f"{course}已选过"
22    
23    def drop_course(self, course):
24        """退课"""
25        if course in self.courses:
26            self.courses.remove(course)
27            return f"{self.name}已退课:{course}"
28        return f"未选{course}"
29    
30    def get_info(self):
31        """获取学生信息"""
32        courses_str = ", ".join(self.courses) if self.courses else "无"
33        return f"""
34学生信息:
35姓名:{self.name}
36学号:{self.student_id}
37年级:{self.grade}
38已选课程:{courses_str}
39"""
40    
41    @classmethod
42    def get_total_count(cls):
43        """获取学生总数(类方法)"""
44        return f"学生总数:{cls.total_count}"
45
46# 使用
47student1 = Student("张三", "S001", 1)
48student2 = Student("李四", "S002", 2)
49
50print(student1.enroll_course("Python编程"))
51print(student1.enroll_course("数据结构"))
52print(student1.get_info())
53
54print(Student.get_total_count())  # 学生总数:2

8. 与Golang的对比

Golang也支持面向对象,但方式不同:

 1// Golang:使用结构体
 2type Dog struct {
 3    Name string
 4    Age  int
 5}
 6
 7// 方法通过接收者定义
 8func (d *Dog) Bark() string {
 9    return d.Name + " says: Woof!"
10}
11
12// 使用
13dog := Dog{Name: "Buddy", Age: 3}
14fmt.Println(dog.Bark())

对比

  • Python:以类为中心,显式self
  • Go:以结构体为中心,方法接收者
  • Python:动态类型,运行时检查
  • Go:静态类型,编译时检查

9. 小结

今天我们学习了OOP基础:

  • 类与对象:模板与实例
  • 定义类:class关键字
  • init:初始化方法
  • self:实例引用
  • 属性:类属性vs实例属性
  • 方法:实例方法
  • 封装:公开、私有、受保护
  • 命名约定___的含义

这是面向对象编程的基础,后续我们将学习继承、多态等高级特性。


练习题

  1. 创建一个Rectangle类,包含长和宽属性,以及计算面积和周长的方法
  2. 创建一个Book类,实现图书管理的基本功能
  3. 对比类属性和实例属性的使用场景

本文代码示例

关注公众号:极客老墨

更多 AI 应用开发、工程实践和效率工具分享,欢迎扫码关注。

极客老墨微信公众号二维码

相关阅读