Python 教程 / 模块 / 面向对象编程

面向对象编程 (OOP)

面向对象编程(Object Oriented Programming,简称OOP)是一种程序设计思想。OOP把对象作为程序的基本单元,一个对象包含了数据操作数据的函数。在Python中,一切皆为对象。

面向过程 vs 面向对象

让我们通过学生成绩管理这个例子,理解两种编程范式的区别:

┌─────────────────────────────────────────────────────────────────┐
│                  面向过程 vs 面向对象 对比                        │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  【面向过程 - 像做菜食谱】                                       │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │  步骤1: 准备学生A的成绩数据                              │   │
│  │  步骤2: 调用 print_score() 打印                          │   │
│  │  步骤3: 准备学生B的成绩数据                              │   │
│  │  步骤4: 调用 print_score() 打印                          │   │
│  │  ...                                                    │   │
│  │                                                         │   │
│  │  特点: 数据和函数分离,函数操作外部数据                   │   │
│  └─────────────────────────────────────────────────────────┘   │
│                                                                 │
│  【面向对象 - 像养宠物】                                         │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │  class Student:                                         │   │
│  │      # 数据(name, score)和方法(print_score)在一起       │   │
│  │                                                         │   │
│  │  学生A = Student("小明", 95)   # 创建对象               │   │
│  │  学生A.print_score()           # 给对象发消息           │   │
│  │                                                         │   │
│  │  特点: 数据和方法封装在一起,对象自己管理自己的数据       │   │
│  └─────────────────────────────────────────────────────────┘   │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘
                         

面向过程方式

Python
# 面向过程:用字典表示学生,用函数处理
std1 = {'name': '小明', 'score': 95}
std2 = {'name': '小红', 'score': 87}

def print_score(std):
    print(f"{std['name']}: {std['score']}")

def get_score(std):
    return std['score']

print_score(std1)  # 小明: 95
print_score(std2)  # 小红: 87

# 问题:如果要添加新功能,比如打印等级,需要修改多个函数
def get_grade(score):
    if score >= 90: return 'A'
    if score >= 80: return 'B'
    return 'C' 

面向对象方式

Python
# 面向对象:把数据和方法封装在类中
class Student:
    def __init__(self, name, score):
        self.name = name
        self.score = score

    def print_score(self):
        print(f"{self.name}: {self.score}")

    def get_grade(self):
        if self.score >= 90: return 'A'
        if self.score >= 80: return 'B'
        return 'C'

# 创建两个学生对象
student1 = Student('小明', 95)
student2 = Student('小红', 87)

student1.print_score()  # 小明: 95
student2.print_score()  # 小红: 87

# 优点:添加新功能只需在类中添加方法,不影响现有代码 
类 vs 实例
类(Class)是抽象的"概念",比如"学生"这个概念;
实例(Instance)是具体的"个体",比如"小明"这个具体的学生。
类是蓝图,实例是根据蓝图创建的实际对象。

类的基本结构

┌─────────────────────────────────────────────────────────────────┐
│                      类(Class)的基本结构                          │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   class 类名:                                                    │
│                                                                 │
│       # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━   │
│       #  类变量 / 类属性 (所有实例共享)                           │
│       # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━   │
│       species = "动物"                                          │
│       count = 0                                                  │
│                                                                 │
│       # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━   │
│       #  初始化方法 __init__                                     │
│       # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━   │
│       def __init__(self, name, age):                           │
│           self.name = name      # 实例变量                       │
│           self.age = age        # 实例变量                       │
│                                                                 │
│       # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━   │
│       #  实例方法 (第一个参数是 self)                             │
│       # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━   │
│       def speak(self):                                          │
│           return f"{self.name}在叫"                            │
│                                                                 │
│       # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━   │
│       #  特殊方法 __str__                                        │
│       # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━   │
│       def __str__(self):                                        │
│           return f"Student({self.name})"                       │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘
                         

1. 定义类

Python
class Dog:
    # 类变量 - 所有实例共享
    species = "犬科"
    total_dogs = 0

    # 初始化方法 - 创建实例时自动调用
    def __init__(self, name, age):
        # self 是实例对象本身
        self.name = name      # 实例变量 - 每个实例独有
        self.age = age
        Dog.total_dogs += 1   # 修改类变量

    # 实例方法 - 第一个参数必须是 self
    def bark(self):
        return f"{self.name}说: 汪汪!"

    # 实例方法可以访问和修改实例变量和类变量
    def info(self):
        return f"{self.name}, {self.age}岁, 是{self.species}"

    # 特殊方法 - 字符串表示
    def __str__(self):
        return f"Dog({self.name})" 

2. 创建实例

Python
# 创建 Dog 类的实例
my_dog = Dog("旺财", 3)
your_dog = Dog("小黑", 5)

# 调用实例方法
print(my_dog.bark())      # 旺财说: 汪汪!
print(your_dog.bark())    # 小黑说: 汪汪!
print(my_dog.info())      # 旺财, 3岁, 是犬科

# 访问类变量
print(Dog.species)        # 犬科 (通过类访问)
print(my_dog.species)     # 犬科 (通过实例访问)

# 类变量是共享的
print(Dog.total_dogs)     # 2

# 使用 __str__ 方法
print(my_dog)            # Dog(旺财) 

3. self 参数详解

self 是类方法的一个特殊参数,它指向当前实例对象。通过 self,方法可以访问和修改实例的属性。

┌─────────────────────────────────────────────────────────────────┐
│                         self 工作原理                             │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   my_dog = Dog("旺财", 3)                                        │
│                                                                 │
│   当调用 my_dog.bark() 时:                                       │
│                                                                 │
│   ┌─────────────────────────────────────────────────────────┐   │
│   │  Python 内部做的是:                                      │   │
│   │                                                         │   │
│   │  Dog.bark(my_dog)   # 转换为类方法调用                   │   │
│   │                                                         │   │
│   │  所以 self.name 在这里就是 "旺财"                        │   │
│   └─────────────────────────────────────────────────────────┘   │
│                                                                 │
│   内存示意:                                                      │
│   ┌─────────────────────────────────────────────────────────┐   │
│   │  my_dog ----→ Dog 实例对象                              │   │
│   │               ┌──────────────────────────────┐           │   │
│   │               │  name = "旺财"              │           │   │
│   │               │  age = 3                    │           │   │
│   │               │  species = "犬科" (共享)     │           │   │
│   │               └──────────────────────────────┘           │   │
│   └─────────────────────────────────────────────────────────┘   │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘
                         

封装 - 数据保护

封装是将数据和操作数据的方法绑定在一起,并隐藏内部实现细节。对外提供接口,限制对内部数据的直接访问。

┌─────────────────────────────────────────────────────────────────┐
│                        封装 (Encapsulation)                      │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   【没有封装】                                                    │
│   ┌─────────────────────────────────────────────────────────┐   │
│   │  user.password = "123456"   # 任何人都可以随意修改!     │   │
│   │  user.balance = -1000       # 可以设置为负数!           │   │
│   └─────────────────────────────────────────────────────────┘   │
│                                                                 │
│   【有封装 - 受保护的数据】                                       │
│   ┌─────────────────────────────────────────────────────────┐   │
│   │  class User:                                            │   │
│   │      def __init__(self):                                │   │
│   │          self.__password = "xxx"   # 私有属性           │   │
│   │          self.__balance = 0          # 私有属性           │   │
│   │                                                         │   │
│   │      def set_balance(self, amount):                     │   │
│   │          if amount < 0:              # 添加验证          │   │
│   │              raise ValueError("不能为负")                 │   │
│   │          self.__balance = amount                          │   │
│   └─────────────────────────────────────────────────────────┘   │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘
                         

访问控制级别

Python
class User:
    def __init__(self, name, age):
        self.name = name           # 公开属性 - 可以在任何地方访问
        self._age = age            # 受保护属性 - 约定不直接外部访问
        self.__password = "123"   # 私有属性 - 会在外部被改名

    # 公开方法访问私有属性
    def get_password(self):
        return "****"     # 不返回真实密码

    def set_password(self, new_pwd):
        if len(new_pwd) < 6:
            raise ValueError("密码长度至少6位")
        self.__password = new_pwd

user = User("张三", 25)

# 访问公开属性
print(user.name)              # 张三

# 访问受保护属性 (可以访问,但不推荐)
print(user._age)              # 25

# 访问私有属性 - 会失败
# print(user.__password)  # AttributeError!

# Python 名称重整后可以访问,但不推荐
print(user._User__password)    # 123 (不推荐)

# 通过公开方法访问
print(user.get_password())      # **** 
Python 的访问控制是"约定"而非"强制"
Python 没有真正的 private 修饰符,但通过名称重整(Name Mangling)机制,__password 会变成 _ClassName__password,在一定程度上提示开发者不要直接访问。

继承 - 代码复用

继承允许我们定义一个类(子类)继承另一个类(父类)的属性和方法,实现代码复用。

┌─────────────────────────────────────────────────────────────────┐
│                        继承关系示意图                              │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│                        【动物 Animal】                            │
│                        - name                                    │
│                        - eat()                                   │
│                        - speak()                                 │
│                              ▲                                   │
│                              │ 继承                               │
│              ┌───────────────┴───────────────┐                  │
│              │                               │                   │
│         【狗 Dog】                      【猫 Cat】                │
│         - breed                           - color                │
│         - bark() ← 重写 speak()           - meow() ← 重写 speak()│
│                                                                 │
│   继承语法:                                                     │
│   class 子类(父类):                                             │
│       pass                                                     │
│                                                                 │
│   子类会自动获得父类的所有属性和方法                               │
│   子类可以重写(override)父类的方法                               │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘
                         

1. 基本继承

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

    def eat(self):
        return f"{self.name}正在吃东西"

    def speak(self):
        pass  # 子类必须重写此方法

# 子类 (派生类)
class Dog(Animal):
    # 子类可以添加自己的属性
    def __init__(self, name, breed):
        # 调用父类的 __init__
        super().__init__(name)
        self.breed = breed

    # 重写父类的方法
    def speak(self):
        return f"{self.name}说: 汪汪!"

    # 子类新增的方法
    def guard(self):
        return f"{self.name}正在看家"

# 创建子类实例
dog = Dog("旺财", "金毛")

# 继承自父类的方法
print(dog.eat())        # 旺财正在吃东西

# 子类重写的方法
print(dog.speak())      # 旺财说: 汪汪!

# 子类自己的方法
print(dog.guard())     # 旺财正在看家

# 子类的属性
print(dog.breed)       # 金毛 

2. super() 函数

super() 允许子类调用父类的方法,实现代码复用。

Python
class Animal:
    def __init__(self, name, age):
        self.name = name
        self.age = age

class Dog(Animal):
    def __init__(self, name, age, breed):
        # 调用父类的 __init__,复用父类的初始化逻辑
        super().__init__(name, age)
        self.breed = breed

class Cat(Animal):
    def __init__(self, name, age, color):
        super().__init__(name, age)  # 同样调用父类
        self.color = color

# 测试
dog = Dog("旺财", 3, "金毛")
cat = Cat("咪咪", 2, "白色")

print(f"狗: {dog.name}, {dog.age}岁, {dog.breed}")
print(f"猫: {cat.name}, {cat.age}岁, {cat.color}") 

3. 多继承

Python 支持多继承,一个子类可以继承多个父类。

Python
# 多个父类
class Flyable:
    def fly(self):
        return f"{self.name}正在飞"

class Swimable:
    def swim(self):
        return f"{self.name}正在游泳"

# 子类继承多个父类
class Duck(Flyable, Swimable):
    def __init__(self, name):
        self.name = name

duck = Duck("唐老鸭")
print(duck.fly())   # 唐老鸭正在飞
print(duck.swim())  # 唐老鸭正在游泳

# 方法解析顺序 (MRO)
print(Duck.__mro__) 

多态 - 同一接口,不同实现

多态允许我们使用父类类型的引用指向子类对象,调用时根据实际对象类型决定执行哪个方法。

┌─────────────────────────────────────────────────────────────────┐
│                        多态 (Polymorphism)                       │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   【概念】同一个接口,不同的实现                                   │
│                                                                 │
│       ┌─────────┐                                              │
│       │ speak() │  ← 统一接口                                   │
│       └────┬────┘                                              │
│            │                                                   │
│     ┌──────┼──────┐                                           │
│     ▼      ▼      ▼                                           │
│   狗叫    猫叫    鸟叫                                          │
│   "汪汪"  "喵喵"  "叽叽"                                        │
│                                                                 │
│   【优势】代码灵活,易于扩展                                      │
│                                                                 │
│   def make_speak(animal):                                      │
│       print(animal.speak())   # 不需要知道具体是什么动物         │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘
                         
Python
class Animal:
    def __init__(self, name):
        self.name = name

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

class Dog(Animal):
    def speak(self):
        return f"{self.name}说: 汪汪!"

class Cat(Animal):
    def speak(self):
        return f"{self.name}说: 喵喵!"

class Duck(Animal):
    def speak(self):
        return f"{self.name}说: 嘎嘎!"

# 多态:同一个函数,传入不同对象,执行不同行为
def make_speak(animals):
    for animal in animals:
        print(animal.speak())

# 创建不同类型的动物
animals = [Dog("旺财"), Cat("咪咪"), Duck("唐老鸭")]

# 调用同一个函数,显示不同结果
make_speak(animals)
# 输出:
# 旺财说: 汪汪!
# 咪咪说: 喵喵!
# 唐老鸭说: 嘎嘎! 
多态的优势
1. 可扩展性:添加新动物类型时,不需要修改 make_speak() 函数
2. 解耦合:调用者只需要知道 speak() 方法存在,不需要关心具体类型
3. 灵活性:同一份代码,可以处理不同类型的对象

特殊方法 (Magic Methods)

Python 类有一系列以双下划线开头和结尾的特殊方法,也称为"魔法方法",用于实现特定功能。

Python
class Vector:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    # 字符串表示 - print() 和 str() 使用
    def __str__(self):
        return f"Vector({self.x}, {self.y})"

    # 正式字符串表示 - repr() 使用
    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 __len__(self):
        return 2

    # 索引访问
    def __getitem__(self, index):
        if index == 0: return self.x
        if index == 1: return self.y
        raise IndexError("索引超出范围")

v1 = Vector(2, 3)
v2 = Vector(4, 5)

print(v1)               # Vector(2, 3)
print(v1 + v2)         # Vector(6, 8)
print(len(v1))           # 2
print(v1[0], v1[1])   # 2 3 
特殊方法 作用 示例
__init__ 初始化实例 创建对象时自动调用
__str__ 字符串表示 print(obj) 时调用
__repr__ 正式字符串 repr(obj) 时调用
__add__ 加法运算 obj1 + obj2
__len__ 长度 len(obj)
__eq__ 相等比较 obj1 == obj2

知识测验

问题1:以下代码的输出是什么?

class A:
    x = 1

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

a = A(10)
print(a.x, a.y) 

问题2:关于继承和多态,哪个说法是正确的?

问题3:以下哪个是私有属性的正确写法?

课后练习

✏️ 练习1:设计银行账户类

设计一个银行账户类:

  • 属性:账号、姓名、余额(私有)
  • 方法:存款(验证金额为正)、取款(验证余额充足)、查询余额
  • 使用封装保护余额不被直接修改
  • 添加转账功能(从本账户转出到另一个账户)
✏️ 练习2:图形继承体系

设计一个图形继承体系:

  • 基类 Shape,有属性 name 和方法 area()、perimeter()
  • 子类 Circle(需要半径)、Rectangle(需要长宽)
  • 子类 Square(继承 Rectangle,只需要一个边长)
  • 创建多态函数 print_shape_info(),接收任意 Shape 对象
✏️ 练习3:实现特殊方法

创建一个复数类 Complex:

  • 属性:实部 real,虚部 imag
  • 实现 __add__(复数加法)
  • 实现 __str__(显示格式如 3+4j)
  • 实现 __eq__(判断两个复数是否相等)
  • 测试复数的加减和比较

章节总结

┌─────────────────────────────────────────────────────────────────┐
│                        本章要点                                  │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  1. 面向对象概念                                                 │
│     • 类(Class):抽象模板,定义对象的共同属性和方法               │
│     • 实例(Instance):根据类创建的具体对象                        │
│     • OOP三要素:封装、继承、多态                                 │
│                                                                 │
│  2. 类定义                                                      │
│     • class 类名:                                                │
│     • __init__():初始化方法                                     │
│     • self:指向实例本身的引用                                    │
│                                                                 │
│  3. 继承                                                        │
│     • class 子类(父类):                                         │
│     • super().__init__():调用父类构造方法                       │
│     • 多继承:class 子类(父类1, 父类2):                          │
│                                                                 │
│  4. 多态                                                        │
│     • 同一接口,不同实现                                         │
│     • 增强代码的可扩展性和灵活性                                  │
│                                                                 │
│  5. 访问控制                                                    │
│     • 公开:name                                                │
│     • 受保护(约定):_name                                      │
│     • 私有(重整):__name                                      │
│                                                                 │
│  6. 特殊方法                                                     │
│     • __str__:字符串表示                                       │
│     • __repr__:正式字符串                                       │
│     • __add__:加法运算                                         │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘
                             
🐍 Python 在线代码编辑器
📝 运行结果