Python 学习笔记(第八期)——面向对象编程:类、继承与多态

更多Python知识:Python基础入门

我的主页:AOwhisky,这里有更多运维系统性知识整理和其他有趣内容,欢迎与我一起探讨学习~

📖 引言:从“执行者”到“组织者”

在前七期中,我们一直采用面向过程的编程方式——把程序拆解为一系列步骤,用函数封装每个步骤,然后按顺序调用。这种方式适合解决相对简单的问题,就像照着菜谱一步步做菜——步骤清晰,但灵活性有限。

但随着程序规模的增长,面向过程的局限性逐渐暴露。假设你要开发一个“校园管理系统”,涉及学生、教师、课程、班级等多个实体,每个实体都有自己的属性和行为。如果还是用函数加数据结构的方式,代码会变得散乱,维护起来非常困难。

这时候,面向对象编程(OOP) 就派上了用场。OOP 的核心思想是:把程序中的事物抽象为“对象”,每个对象既有数据(属性),又有行为(方法)。对象之间通过相互协作来完成程序的功能。

💡 白话理解:面向过程编程像是“导演写剧本”——一切按步骤执行;面向对象编程像是“制片人搭建剧组”——每个角色(对象)都有自己的职责,角色之间通过沟通协作完成整部戏。

— Compiled and Authored by Whisky — July 16 th, 2026

📑 本期目录

  1. 面向对象概述(面向过程 vs 面向对象)
  2. 类的定义与使用(属性与方法)
  3. 类的成员(类属性、实例属性、方法)
  4. 特殊方法(构造方法与析构方法)
  5. 封装(私有成员与公有接口)
  6. 继承(单继承、多继承、方法重写)
  7. 多态(同一接口,不同行为)
  8. 运算符重载(让自定义对象支持运算符)
  9. 综合实战演练(两个完整案例)
  10. 总结与知识点一览表

正文

一、面向对象概述

1.1 面向过程 vs 面向对象

在开始学习 OOP 之前,我们先通过一个日常例子理解两种编程范式的区别。

场景:制作一杯美式咖啡。

面向过程方式——关注“步骤”:

  1. 准备材料:水、咖啡粉
  2. 将水加热至沸腾
  3. 在滤杯中放置滤纸
  4. 用热水冲泡咖啡粉
  5. 等待过滤完成
  6. 得到一杯咖啡
代码结构:函数1() → 函数2() → 函数3() → ... → 完成

面向对象方式——关注“角色”:

  • 咖啡机:有“煮制”功能,能接收水和咖啡粉
  • :有“温度”属性,可以被加热
  • 咖啡粉:有“研磨程度”属性
  • 杯子:有“容量”属性,用于盛装咖啡
代码结构:咖啡机.煮制(水, 咖啡粉) → 得到咖啡

💡 核心区别

  • 面向过程:我要做什么步骤?(以动作为中心)
  • 面向对象:谁来做这件事?(以事物为中心)

面向对象的优势

优势 说明 示例
可维护性 修改某个对象时,不影响其他对象 想换咖啡机品牌?只需替换“咖啡机”对象
代码复用 通过继承,子类自动拥有父类功能 想喝茶?直接继承“饮品制作”类,替换原料即可
扩展性 新增功能时,只需新增类或方法 想增加“拿铁”品类?新增类即可
1.2 类和对象

面向对象编程有两个核心概念:

概念 定义 类比
类(Class) 一类事物的抽象描述,定义共同的特征和行为 像“蓝图”或“模板”
对象(Object) 类的具体实例,真实存在的个体 像按蓝图盖出来的“房子”
类:人类(Person)—— 共性:有姓名、年龄,能说话、能走路
          ↓ 实例化
对象:张三 —— 具体的人,姓名=张三,年龄=18
对象:李四 —— 具体的人,姓名=李四,年龄=20

💡 白话理解:类就像是“饼干模具”,对象是用模具做出来的“饼干”。同一套模具可以做出一批形状相同的饼干,每块饼干都是一个独立的对象。

二、类的定义与使用

2.1 类的定义

类由三部分组成:

组成部分 说明 示例
类名 标识类,使用大驼峰命名法 PersonStudentInfo
属性 描述事物的静态特征 姓名、年龄、身高
方法 描述事物的动态行为 说话、走路、学习
# 定义类
class Car:
    """汽车类"""
    # 类属性(所有实例共享)
    wheels = 4

    # 方法(行为)
    def drive(self):
        print("汽车正在行驶")

    def stop(self):
        print("汽车已停止")
2.2 对象的创建与使用
# 创建对象(实例化)
car1 = Car()
car2 = Car()

# 访问属性
print(car1.wheels)   # 4

# 调用方法
car1.drive()         # 汽车正在行驶
car1.stop()          # 汽车已停止

类的结构图

┌─────────────────────────┐
│          Car             │  ← 类名
├─────────────────────────┤
│  wheels = 4             │  ← 类属性(静态特征)
├─────────────────────────┤
│  def drive(self):       │  ← 方法(动态行为)
│      print("行驶")      │
│  def stop(self):        │
│      print("停止")      │
└─────────────────────────┘
        ↓ 实例化
┌─────────────┐  ┌─────────────┐
│   car1      │  │   car2      │  ← 对象(具体实例)
│  wheels=4   │  │  wheels=4   │
│  能行驶     │  │  能行驶     │
│  能停止     │  │  能停止     │
└─────────────┘  └─────────────┘

三、类的成员

类的成员分为属性方法两大类。

3.1 属性(类属性 vs 实例属性)
类型 定义位置 访问方式 修改影响
类属性 类内部、方法外部 通过类或对象访问 通过类修改影响所有实例
实例属性 方法内部(用 self 只能通过对象访问 每个实例独立,互不影响
class Car:
    # 类属性(所有实例共享)
    wheels = 4

    def drive(self):
        # 实例属性(每个实例独立)
        self.speed = 0
        self.color = "白色"

# 1. 访问类属性
print(Car.wheels)      # 4(通过类访问)
print(car1.wheels)     # 4(通过对象访问)

# 2. 通过类修改类属性 → 影响所有实例
Car.wheels = 3
print(car1.wheels)     # 3
print(car2.wheels)     # 3

# 3. 通过对象修改类属性 → 只影响该实例
car1.wheels = 4
print(car1.wheels)     # 4
print(car2.wheels)     # 3(不受影响)

实例属性的动态添加

class Car:
    def drive(self):
        self.speed = 0

car = Car()
car.drive()
print(car.speed)       # 0

# 动态添加新实例属性
car.color = "红色"
print(car.color)       # 红色
3.2 方法(实例方法、类方法、静态方法)
类型 装饰器 第一个参数 访问方式 适用场景
实例方法 self(对象本身) 只能通过对象调用 需要访问或修改实例属性
类方法 @classmethod cls(类本身) 通过类或对象调用 需要访问或修改类属性
静态方法 @staticmethod 通过类或对象调用 与类相关但不需要访问类或实例

实例方法

class Car:
    def drive(self):      # self 代表对象本身
        print("正在行驶")
        print(f"当前对象:{self}")

car = Car()
car.drive()               # 通过对象调用
# Car.drive()             # ❌ 通过类调用会报错

类方法

class Car:
    wheels = 4

    @classmethod
    def show_wheels(cls):   # cls 代表类本身
        print(f"车轮数:{cls.wheels}")
        cls.wheels = 3      # 可以修改类属性

# 通过类或对象都可以调用
Car.show_wheels()           # 车轮数:4
car = Car()
car.show_wheels()           # 车轮数:4

静态方法

class Car:
    @staticmethod
    def test():             # 无特殊参数
        print("这是静态方法,不访问类或实例的属性和方法")

Car.test()                  # 通过类调用
car = Car()
car.test()                  # 通过对象调用

三种方法对比

对比维度 实例方法 类方法 静态方法
能否访问实例属性 ✅ 可以 ❌ 不可以 ❌ 不可以
能否访问类属性 ✅ 可以(通过 self.__class__ ✅ 可以(通过 cls ❌ 不可以(除非用 类名.属性
能否修改类属性 ✅ 可以(通过 self.__class__ ✅ 可以(通过 cls ❌ 不可以
适用场景 操作实例数据的业务逻辑 工厂方法、修改类属性 工具函数,与类相关但无需类数据

四、特殊方法

4.1 构造方法 __init__() ——“初始化器”

构造方法在创建对象时自动调用,用于初始化对象的属性。每个类默认都有一个无参构造方法,我们可以显式定义它。

无参构造方法:所有对象属性有相同的初始值。

class Car:
    def __init__(self):          # 无参构造方法
        self.color = "白色"
        self.speed = 0

    def drive(self):
        print(f"颜色:{self.color},速度:{self.speed}")

car1 = Car()                     # 自动调用 __init__()
car1.drive()                     # 颜色:白色,速度:0

有参构造方法:每个对象可以有不同的初始值。

class Car:
    def __init__(self, color, speed=0):   # 有参构造方法
        self.color = color
        self.speed = speed

    def drive(self):
        print(f"颜色:{self.color},速度:{self.speed}")

car1 = Car("红色")                # 颜色:红色,速度:0
car2 = Car("蓝色", 100)           # 颜色:蓝色,速度:100

car1.drive()                      # 颜色:红色,速度:0
car2.drive()                      # 颜色:蓝色,速度:100

💡 白话理解__init__() 就像“出厂设置”——新车出厂时,需要给它设置颜色、里程等初始状态。不同车有不同的配置,通过构造方法的参数来定制。

4.2 析构方法 __del__() ——“销毁器”

析构方法在对象被销毁时自动调用,用于释放资源(如关闭文件、断开网络连接)。

class Car:
    def __init__(self, color):
        self.color = color
        print(f"{color} 汽车已创建")

    def __del__(self):
        print(f"{self.color} 汽车已销毁")

# 创建对象
car = Car("红色")      # 红色 汽车已创建

# 手动销毁对象
del car                # 红色 汽车已销毁

对象销毁的时机

  • 程序结束时,所有对象被自动销毁
  • 使用 del 语句手动销毁
  • 对象的引用计数器变为 0 时自动销毁

引用计数器机制

car1 = Car("红色")    # 引用计数:1
car2 = car1           # 引用计数:2
car3 = car1           # 引用计数:3

del car1              # 引用计数:2
del car2              # 引用计数:1
del car3              # 引用计数:0 → 对象被销毁

五、封装——把数据“藏起来”

封装是面向对象的三大特性之一(封装、继承、多态),其核心思想是:

对外隐藏类的内部细节,提供公开的访问接口。

5.1 私有成员

在属性或方法名前加双下划线 __,将其变为私有成员。

成员类型 命名方式 访问范围 示例
公有成员 常规命名 任何地方都可以访问 self.name
私有成员 __ 开头 只能在类内部访问 self.__age
class Person:
    def __init__(self, name, age):
        self.name = name           # 公有属性
        self.__age = age           # 私有属性

    def get_age(self):             # 公有方法(访问接口)
        return self.__age

    def set_age(self, new_age):    # 公有方法(修改接口)
        if 0 < new_age <= 120:     # 带合法性检查
            self.__age = new_age
        else:
            print("年龄无效!")

    def __secret(self):            # 私有方法
        print("这是私有方法")

person = Person("张三", 18)

# 公有属性:可以直接访问
print(person.name)       # 张三

# 私有属性:不能直接访问
# print(person.__age)    # ❌ AttributeError

# 通过公有接口访问
print(person.get_age())  # 18

# 通过公有接口修改(带校验)
person.set_age(25)
print(person.get_age())  # 25
person.set_age(200)      # 年龄无效!

💡 白话理解:封装就像银行的“柜台服务”——客户不能直接进入金库(私有属性),而是通过柜台窗口(公有方法)办理业务。这样做的好处是:银行可以在柜台窗口做安全检查(数据校验),确保每一笔操作都合规。

为什么要封装?

原因 说明
数据安全 防止外部直接修改关键数据,造成数据不一致
数据校验 在设置数据时进行合法性检查(如年龄不能为负)
隐藏实现细节 外部只需知道“怎么用”,不需要知道“怎么实现”
降低耦合 修改内部实现时,不影响外部调用代码

六、继承——让代码“遗传”下去

继承是面向对象的第二大特性,它允许一个类继承另一个类的属性和方法,从而实现代码复用。

6.1 单继承

子类继承一个父类,自动拥有父类的公有成员(私有成员不会继承)。

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

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

    def sleep(self):
        print(f"{self.name} 正在睡觉")

# 子类(派生类)
class Dog(Animal):          # 继承 Animal
    def bark(self):         # 添加新方法
        print(f"{self.name} 汪汪叫")

# 子类(派生类)
class Cat(Animal):          # 继承 Animal
    def meow(self):         # 添加新方法
        print(f"{self.name} 喵喵叫")

# 使用
dog = Dog("旺财")
dog.eat()        # 旺财 正在吃东西(继承自父类)
dog.sleep()      # 旺财 正在睡觉(继承自父类)
dog.bark()       # 旺财 汪汪叫(子类特有)

cat = Cat("咪咪")
cat.eat()        # 咪咪 正在吃东西(继承自父类)
cat.meow()       # 咪咪 喵喵叫(子类特有)

继承关系图

        ┌─────────────┐
        │   Animal    │  ← 父类
        │  - name     │
        │  + eat()    │
        │  + sleep()  │
        └──────┬──────┘
               │ 继承
       ┌───────┴───────┐
       ↓               ↓
┌─────────────┐ ┌─────────────┐
│    Dog      │ │    Cat      │  ← 子类
│  + bark()   │ │  + meow()   │
└─────────────┘ └─────────────┘
6.2 多继承

一个子类可以继承多个父类,拥有所有父类的公有成员。

class Flyable:
    def fly(self):
        print("正在飞行")

class Swimmable:
    def swim(self):
        print("正在游泳")

# 多继承:同时继承 Flyable 和 Swimmable
class Duck(Flyable, Swimmable):
    def quack(self):
        print("嘎嘎叫")

duck = Duck()
duck.fly()     # 正在飞行(继承自 Flyable)
duck.swim()    # 正在游泳(继承自 Swimmable)
duck.quack()   # 嘎嘎叫(子类特有)

多继承的方法查找顺序(MRO)

当多个父类有同名方法时,Python 按照方法解析顺序(MRO) 从左到右查找。

class A:
    def hello(self):
        print("A.hello")

class B:
    def hello(self):
        print("B.hello")

class C(A, B):    # 先继承 A,再继承 B
    pass

c = C()
c.hello()         # A.hello(先找 A)

# 查看 MRO 顺序
print(C.__mro__)  # (<class 'C'>, <class 'A'>, <class 'B'>, <class 'object'>)
6.3 方法重写与 super()

子类可以重写父类的方法,实现自己的逻辑。如果需要调用父类的方法,可以使用 super()

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

    def speak(self):
        print("动物发出声音")

class Dog(Animal):
    def speak(self):              # 重写父类方法
        super().speak()           # 调用父类方法
        print("汪汪叫")            # 子类新增逻辑

dog = Dog("旺财")
dog.speak()

运行结果

动物发出声音
汪汪叫

super() 的主要作用

场景 说明
调用父类构造方法 在子类 __init__() 中调用 super().__init__() 初始化父类属性
扩展父类方法 在子类方法中先调用父类方法,再添加额外逻辑
多继承中调用特定父类方法 按 MRO 顺序调用父类方法
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

class Student(Person):
    def __init__(self, name, age, grade):
        super().__init__(name, age)   # 调用父类构造方法
        self.grade = grade            # 子类新增属性

    def info(self):
        print(f"姓名:{self.name},年龄:{self.age},年级:{self.grade}")

stu = Student("张三", 18, "高三")
stu.info()   # 姓名:张三,年龄:18,年级:高三

七、多态——同一种行为,不同的表现

多态是面向对象的第三大特性,它的核心含义是:

不同类的对象对同一方法调用,表现出不同的行为。

多态的前提条件

  1. 存在继承关系
  2. 子类重写父类的方法
  3. 使用父类类型的引用调用被子类重写的方法
class Animal:
    def speak(self):
        pass

class Dog(Animal):
    def speak(self):
        print("汪汪叫")

class Cat(Animal):
    def speak(self):
        print("喵喵叫")

class Duck(Animal):
    def speak(self):
        print("嘎嘎叫")

# 多态:同一个函数,传入不同的对象,表现出不同的行为
def animal_speak(animal: Animal):
    animal.speak()

# 测试
dog = Dog()
cat = Cat()
duck = Duck()

animal_speak(dog)    # 汪汪叫
animal_speak(cat)    # 喵喵叫
animal_speak(duck)   # 嘎嘎叫

运行结果

汪汪叫
喵喵叫
嘎嘎叫

💡 白话理解:多态就像“开会发言”——同样是在会议上“说话”,老板说的是战略,技术说的是架构,销售说的是业绩。同一句话“说话”,不同角色有不同的表现。

八、运算符重载

运算符重载允许我们为自定义类定义运算符的行为,使对象支持 +-*in 等运算。

特殊方法 对应运算符 说明
__add__(self, other) + 加法
__sub__(self, other) - 减法
__mul__(self, other) * 乘法
__truediv__(self, other) / 除法
__mod__(self, other) % 取余
__pow__(self, other) ** 幂运算
__eq__(self, other) == 相等判断
__lt__(self, other) < 小于判断
__contains__(self, item) in 成员判断
class Vector:
    """二维向量类"""
    def __init__(self, x, y):
        self.x = x
        self.y = 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):
        """重载 * 运算符:向量数乘"""
        return Vector(self.x * scalar, self.y * scalar)

    def __eq__(self, other):
        """重载 == 运算符:向量相等判断"""
        return self.x == other.x and self.y == other.y

    def __str__(self):
        """重载 str() 输出"""
        return f"Vector({self.x}, {self.y})"

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

v3 = v1 + v2        # 调用 __add__
print(v3)           # Vector(4, 6)

v4 = v2 - v1        # 调用 __sub__
print(v4)           # Vector(2, 2)

v5 = v1 * 3         # 调用 __mul__
print(v5)           # Vector(3, 6)

print(v1 == v2)     # False

运行结果

Vector(4, 6)
Vector(2, 2)
Vector(3, 6)
False

💡 实战意义:运算符重载让自定义类的对象能够像内置类型一样使用运算符,代码更加直观。比如向量、矩阵、复数的数学运算,用 + 比用 add() 方法更自然。

九、综合实战演练

实战一:好友管理系统

需求描述:实现一个基于面向对象思想的好友管理系统,支持以下功能:

  1. 添加好友:输入姓名和备注,添加到好友列表
  2. 删除好友:输入姓名,从列表中删除
  3. 备注好友:修改好友的备注名
  4. 展示好友:列出所有好友及其备注
  5. 好友分组:按分组显示好友

涉及知识点:类的定义、属性、方法、封装、字典操作

class Friend:
    """好友类"""
    def __init__(self, name, nickname="", group="默认"):
        self.name = name
        self.nickname = nickname if nickname else name
        self.group = group

    def update_nickname(self, new_nickname):
        """修改备注"""
        self.nickname = new_nickname

    def update_group(self, new_group):
        """修改分组"""
        self.group = new_group

    def __str__(self):
        return f"{self.nickname}{self.name})"

class FriendManager:
    """好友管理类"""
    def __init__(self):
        self.friends = []          # 好友列表
        self.groups = {}           # 分组映射:分组名 → 好友列表

    def add_friend(self, name, nickname="", group="默认"):
        """添加好友"""
        # 检查是否已存在
        for f in self.friends:
            if f.name == name:
                print(f"❌ 好友 {name} 已存在!")
                return False

        friend = Friend(name, nickname, group)
        self.friends.append(friend)

        # 添加到分组
        if group not in self.groups:
            self.groups[group] = []
        self.groups[group].append(friend)

        print(f"✅ 已添加好友:{friend}")
        return True

    def delete_friend(self, name):
        """删除好友"""
        for friend in self.friends:
            if friend.name == name:
                self.friends.remove(friend)
                # 从分组中删除
                for group, members in self.groups.items():
                    if friend in members:
                        members.remove(friend)
                print(f"✅ 已删除好友:{name}")
                return True
        print(f"❌ 未找到好友:{name}")
        return False

    def update_nickname(self, name, new_nickname):
        """修改备注"""
        for friend in self.friends:
            if friend.name == name:
                friend.update_nickname(new_nickname)
                print(f"✅ 已修改备注:{name}{new_nickname}")
                return True
        print(f"❌ 未找到好友:{name}")
        return False

    def update_group(self, name, new_group):
        """修改分组"""
        for friend in self.friends:
            if friend.name == name:
                # 从旧分组移除
                for group, members in self.groups.items():
                    if friend in members:
                        members.remove(friend)
                # 添加到新分组
                if new_group not in self.groups:
                    self.groups[new_group] = []
                self.groups[new_group].append(friend)
                friend.update_group(new_group)
                print(f"✅ 已修改分组:{name}{new_group}")
                return True
        print(f"❌ 未找到好友:{name}")
        return False

    def show_all(self):
        """展示所有好友"""
        if not self.friends:
            print("📭 暂无好友")
            return

        print("\n" + "=" * 40)
        print("          📋 所有好友")
        print("=" * 40)
        for friend in self.friends:
            print(f"  {friend}(分组:{friend.group})")
        print("=" * 40)

    def show_by_group(self):
        """按分组展示好友"""
        if not self.groups:
            print("📭 暂无分组")
            return

        print("\n" + "=" * 40)
        print("          📂 按分组展示")
        print("=" * 40)
        for group, members in self.groups.items():
            print(f"\n【{group}】")
            if not members:
                print("  空")
            else:
                for friend in members:
                    print(f"  - {friend}")
        print("\n" + "=" * 40)

    def menu(self):
        """显示菜单"""
        menu = """
        ==========================
            📱 好友管理系统
        ==========================
        1. 添加好友
        2. 删除好友
        3. 修改备注
        4. 修改分组
        5. 查看所有好友
        6. 按分组查看
        0. 退出
        ==========================
        """
        print(menu)

    def run(self):
        """运行主程序"""
        while True:
            self.menu()
            choice = input("请选择功能(0-6):")

            if choice == "0":
                print("👋 再见!")
                break
            elif choice == "1":
                name = input("请输入好友姓名:").strip()
                nickname = input("请输入备注(可直接回车跳过):").strip()
                group = input("请输入分组(默认:默认):").strip() or "默认"
                self.add_friend(name, nickname, group)
            elif choice == "2":
                name = input("请输入要删除的姓名:").strip()
                self.delete_friend(name)
            elif choice == "3":
                name = input("请输入好友姓名:").strip()
                new_nickname = input("请输入新备注:").strip()
                self.update_nickname(name, new_nickname)
            elif choice == "4":
                name = input("请输入好友姓名:").strip()
                new_group = input("请输入新分组:").strip()
                self.update_group(name, new_group)
            elif choice == "5":
                self.show_all()
            elif choice == "6":
                self.show_by_group()
            else:
                print("❌ 无效选项,请重新选择!")

# 运行
if __name__ == "__main__":
    manager = FriendManager()
    # 添加示例数据
    manager.add_friend("刘备", "刘皇叔", "三国")
    manager.add_friend("关羽", "关云长", "三国")
    manager.run()
实战二:人机猜拳游戏

需求描述:实现一个基于面向对象的人机猜拳游戏,支持以下功能:

  1. 玩家可以选择石头、剪刀、布
  2. 电脑随机出拳
  3. 判断胜负并记录得分
  4. 支持多轮游戏

涉及知识点:类的定义、继承、多态、封装、随机数

import random

class Player:
    """玩家基类"""
    def __init__(self, name):
        self.name = name
        self.score = 0
        self.gesture = None

    def choose_gesture(self):
        """选择手势(子类重写)"""
        pass

    def win(self):
        self.score += 1

    def __str__(self):
        return f"{self.name}{self.score}分"

class HumanPlayer(Player):
    """人类玩家"""
    GESTURE_MAP = {"1": "石头", "2": "剪刀", "3": "布"}

    def choose_gesture(self):
        print("\n请选择出拳:")
        print("1:石头 ✊")
        print("2:剪刀 ✌️")
        print("3:布 🖐️")
        choice = input("请输入编号(1-3):")
        self.gesture = self.GESTURE_MAP.get(choice)
        if not self.gesture:
            print("❌ 无效输入,请重新选择!")
            return self.choose_gesture()
        print(f"你出了:{self.gesture}")
        return self.gesture

class ComputerPlayer(Player):
    """电脑玩家"""
    GESTURES = ["石头", "剪刀", "布"]

    def choose_gesture(self):
        self.gesture = random.choice(self.GESTURES)
        print(f"电脑出了:{self.gesture}")
        return self.gesture

class Game:
    """猜拳游戏类"""
    def __init__(self, rounds=3):
        self.human = HumanPlayer("玩家")
        self.computer = ComputerPlayer("电脑")
        self.rounds = rounds
        self.win_rules = {
            ("石头", "剪刀"): "玩家",
            ("剪刀", "布"): "玩家",
            ("布", "石头"): "玩家",
            ("剪刀", "石头"): "电脑",
            ("布", "剪刀"): "电脑",
            ("石头", "布"): "电脑",
        }

    def judge(self, human_gesture, computer_gesture):
        """判断胜负"""
        if human_gesture == computer_gesture:
            return "平局"

        result = self.win_rules.get((human_gesture, computer_gesture))
        if result == "玩家":
            self.human.win()
            return "你赢了!🎉"
        else:
            self.computer.win()
            return "电脑赢了!😢"

    def play_round(self, round_num):
        """进行一轮游戏"""
        print(f"\n{'=' * 40}")
        print(f"       第 {round_num} 轮")
        print('=' * 40)

        # 双方出拳
        human_gesture = self.human.choose_gesture()
        computer_gesture = self.computer.choose_gesture()

        # 判断胜负
        result = self.judge(human_gesture, computer_gesture)
        print(f"结果:{result}")

    def show_score(self):
        """显示得分"""
        print("\n" + "=" * 40)
        print("        📊 当前比分")
        print("=" * 40)
        print(f"玩家:{self.human.score} 分")
        print(f"电脑:{self.computer.score} 分")
        if self.human.score > self.computer.score:
            print("🏆 玩家领先!")
        elif self.human.score < self.computer.score:
            print("💻 电脑领先!")
        else:
            print("🤝 平局!")
        print("=" * 40)

    def start(self):
        """开始游戏"""
        print("=" * 40)
        print("        ✊ 人机猜拳游戏 ✊")
        print("=" * 40)
        print(f"一共进行 {self.rounds} 轮")

        for i in range(1, self.rounds + 1):
            self.play_round(i)
            self.show_score()

        # 最终结果
        print("\n" + "=" * 40)
        print("        🏁 游戏结束")
        print("=" * 40)
        if self.human.score > self.computer.score:
            print("🎉 恭喜你获得最终胜利!")
        elif self.human.score < self.computer.score:
            print("😢 很遗憾,电脑获胜!")
        else:
            print("🤝 最终平局!")
        print("=" * 40)

# 运行
if __name__ == "__main__":
    game = Game(rounds=3)
    game.start()

运行示例

========================================
        ✊ 人机猜拳游戏 ✊
========================================
一共进行 3 轮

========================================
        第 1 轮
========================================

请选择出拳:
1:石头 ✊
2:剪刀 ✌️
3:布 🖐️
请输入编号(1-3):1
你出了:石头
电脑出了:布
结果:电脑赢了!😢

========================================
        📊 当前比分
========================================
玩家:0 分
电脑:1 分
💻 电脑领先!
========================================

...(后续轮次)

📝 总结与知识点一览表

核心知识点汇总

类别 知识点 关键语法
类定义 使用 class 定义类 class ClassName:
对象创建 实例化类 obj = ClassName()
类属性 所有实例共享 class_var = value
实例属性 每个实例独立 self.var = value
实例方法 操作实例数据 def method(self):
类方法 操作类数据 @classmethod
静态方法 工具函数 @staticmethod
构造方法 初始化对象 __init__(self, ...)
析构方法 销毁对象 __del__(self)
私有成员 双下划线开头 __private_var
封装 私有属性 + 公有方法 隐藏内部实现
单继承 继承一个父类 class Sub(Parent):
多继承 继承多个父类 class Sub(P1, P2):
方法重写 覆盖父类方法 同名方法重新实现
super() 调用父类方法 super().method()
多态 同一接口不同行为 父类引用调用子类方法
运算符重载 自定义运算符行为 __add____eq__

常用语法速查

需求 代码示例
定义类 class Person:
创建对象 p = Person()
定义类属性 species = "human"
定义实例属性 self.name = name
定义实例方法 def speak(self): print("Hello")
定义类方法 @classmethod def create(cls):
定义静态方法 @staticmethod def util():
私有属性 self.__password = "xxx"
构造方法 def __init__(self, name):
继承父类 class Student(Person):
调用父类构造方法 super().__init__(name)
方法重写 def speak(self): pass
运算符重载 def __add__(self, other):

面向对象三大特性总结

特性 定义 实现方式 作用
封装 隐藏内部细节,提供公开接口 私有属性 + 公有方法 保护数据安全,降低耦合
继承 子类自动拥有父类成员 class Sub(Parent): 代码复用,建立层次关系
多态 同一接口,不同实现 方法重写 + 父类引用 扩展性强,接口统一

常见错误排查指南

错误现象 大概率原因 解决方案
TypeError: __init__() missing 1 required positional argument 创建对象时未传入必要参数 检查构造方法的参数,传入正确的参数
AttributeError: 'X' object has no attribute 'y' 访问了不存在的属性或方法 检查属性名是否正确(注意拼写和大小写)
TypeError: method() takes 0 positional arguments but 1 was given 定义实例方法时忘了写 self 实例方法的第一个参数必须为 self
AttributeError: 'super' object has no attribute 'xxx' 父类中不存在该方法 确认父类是否有该方法,或 MRO 顺序是否符合预期
私有属性在外部无法访问 双下划线 __ 开头的属性是私有的 添加公有方法(getter/setter)来访问和修改
TypeError: unsupported operand type(s) for + 自定义类的对象不支持 + 运算 添加 __add__ 方法重载运算符

学习/生产最佳实践

  1. 类名使用大驼峰命名法:如 PersonStudentInfo,方法与属性使用小写 + 下划线。
  2. 善用封装保护数据:将关键属性设为私有,通过公有方法访问和修改。数据校验放在 setter 方法中。
  3. 优先使用组合而非多继承:多继承虽然功能强大,但容易导致 MRO 混乱。在复杂场景中,优先考虑“组合”(将一个类的实例作为另一个类的属性)。
  4. 在每个类中添加 __str__() 方法:便于调试和打印对象信息。
class Person:
    def __str__(self):
        return f"Person(name={self.name}, age={self.age})"
  1. 合理使用 super():在子类中调用父类方法时,使用 super() 而非显式写出父类名,这样在多继承中能保证 MRO 正确。
  2. 方法重写时遵循 LSP:子类重写父类方法时,应保持方法签名(参数个数、返回类型)兼容,不改变方法的预期行为。

🔜 下期预告

本期我们系统学习了面向对象编程——从类与对象的定义、属性与方法,到封装、继承、多态三大特性,再到运算符重载等高级特性。通过“好友管理系统”和“人机猜拳游戏”两个实战项目,将面向对象思想应用到实际场景中。

下一期 ,我们将进入 异常处理 的学习——程序运行过程中不可避免地会出现错误,如何优雅地捕获和处理这些异常,是编写健壮程序的关键。我们将学习 try-except 语句、elsefinally 子句、raise 主动抛出异常、assert 断言以及自定义异常类,让程序在面对错误时不再“崩溃”,而是能够“优雅地恢复”。敬请期待!


— Compiled and Authored by Whisky — July 16 th, 2026
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐