2026/7/30 4:10:47

Python面向对象编程实战:从类与对象到三大特性与项目应用

Python面向对象编程实战:从类与对象到三大特性与项目应用 1. 项目概述从“写脚本”到“搭积木”的思维跃迁很多朋友学Python一开始都是从几行print到用for循环处理列表再到写个函数封装重复逻辑。这个过程很顺畅因为它是线性的、过程化的思维——先做什么再做什么就像写一份烹饪食谱。但当你开始接触稍微复杂点的项目比如要做一个简单的图书管理系统或者一个游戏里要管理多个角色和道具时光靠一堆函数和列表字典代码很快就会变得像一团乱麻改一处而动全身。这时候你就需要“面向对象编程”这个新工具了。你可以把OOP想象成乐高积木。之前写脚本你是在一块底板上直接堆砌零件而OOP则是先设计好各种标准化的积木块类比如2x4的蓝色砖块、带轮子的底盘、窗户模块。每个积木块类都有自己固定的形状属性和可以连接的方式方法。当你需要搭建一辆小车对象时你不需要从零开始切割塑料而是拿出几个设计好的积木块组合一下就行。如果你想再搭一座房子很多积木块如窗户、门是可以复用的。OOP的核心价值就在于此它通过“类”和“对象”把数据和操作数据的方法捆绑在一起让代码结构更清晰、更容易复用和扩展。这个教程的目标就是帮你跨过从“写脚本”到“搭积木”的这道坎。我们不空谈“封装、继承、多态”这些大词而是通过一系列具体的、可运行的示例操作让你亲手把积木块搭起来感受OOP如何让代码变得更优雅、更强大。无论你是想自己写个小工具还是未来想涉足Web开发、数据分析或自动化测试OOP都是你必须熟练掌握的基础。2. 核心概念拆解类、对象、属性与方法在动手写代码之前我们必须把几个核心的“乐高术语”搞清楚。这部分是地基理解透了后面盖房子才稳。2.1 类与对象蓝图与实物这是OOP中最核心的一对关系。类是蓝图、是模具、是抽象的模板。对象是根据这个蓝图制造出来的具体实物、实例。举个例子“汽车”是一个类。这个类描述了所有汽车共有的特征有轮子、有发动机、能跑。但“汽车”这个类本身你不能开走。而根据“汽车”这个蓝图生产出来的“我的那辆红色特斯拉Model 3车牌号是京A·12345”这就是一个具体的对象。在Python里我们用class关键字来定义蓝图类然后用类名加括号的方式来“生产”实物对象。# 定义“汽车”这个蓝图类 class Car: pass # pass表示暂时空着我们先定义一个最简单的类 # 根据蓝图生产两辆具体的汽车对象 my_car Car() your_car Car() print(my_car) # 输出类似__main__.Car object at 0x0000012345ABCDEF print(your_car) # 输出另一个地址 print(my_car your_car) # 输出False你看my_car和your_car虽然都是Car类造出来的但它们是两个独立的对象存储在内存的不同位置。这就是类和对象最直观的区别。2.2 属性对象的“特征”或“状态”属性就是对象身上携带的数据用来描述它的状态。比如一辆车的颜色、品牌、当前速度。在类内部我们通常在一个叫做__init__的特殊方法里初始化对象的属性。__init__可以读作“双下划线init双下划线”我们叫它构造方法。当使用Car()创建对象时Python会自动调用这个方法。class Car: # __init__方法用于初始化新对象的属性 def __init__(self, brand, color): # self 代表即将创建的那个对象本身 self.brand brand # 给对象添加一个属性叫brand值是传入的brand参数 self.color color # 给对象添加一个属性叫color self.speed 0 # 给对象添加一个属性叫speed初始值为0 # 创建对象时需要传入__init__方法中除self外的参数 my_tesla Car(brand“Tesla”, color“red”) your_toyota Car(“Toyota”, “white”) print(f“我的车是{my_tesla.color}色的{my_tesla.brand}。”) # 输出我的车是red色的Tesla。 print(f“当前速度{my_tesla.speed} km/h”) # 输出当前速度0 km/h注意self这个参数是Python的约定它代表实例对象自身。在类的方法内部我们通过self.属性名来访问或修改这个对象的属性。在调用方法时你不需要手动传递self参数Python会自动处理。2.3 方法对象的“行为”或“能力”方法是定义在类内部的函数它描述了对象能做什么。比如车能加速、能刹车、能鸣笛。class Car: def __init__(self, brand, color): self.brand brand self.color color self.speed 0 # 定义一个“加速”方法 def accelerate(self, increment): 让车加速 self.speed increment print(f“{self.color} {self.brand} 加速中... 当前速度{self.speed} km/h”) # 定义一个“刹车”方法 def brake(self, decrement): 让车减速 if self.speed decrement: self.speed - decrement else: self.speed 0 print(f“{self.color} {self.brand} 刹车中... 当前速度{self.speed} km/h”) # 定义一个“鸣笛”方法 def honk(self): 鸣笛 print(f“{self.color} {self.brand}: 嘀嘀”) # 使用对象的方法 my_car Car(“BMW”, “blue”) my_car.accelerate(30) # 输出blue BMW 加速中... 当前速度30 km/h my_car.honk() # 输出blue BMW: 嘀嘀 my_car.brake(15) # 输出blue BMW 刹车中... 当前速度15 km/h方法和普通函数的关键区别在于方法的第一个参数永远是self通过它方法可以访问和操作调用它的那个对象的所有属性。调用时写成对象.方法名(参数)。2.4self详解对象自身的引用self可能是新手最困惑的点。你可以把它理解成每个方法自带的一个“内部指针”永远指向调用这个方法的那个对象自己。当你在类外部写my_car.accelerate(30)时Python实际上悄悄做了这件事Car.accelerate(my_car, 30)。它把my_car这个对象作为第一个参数即self传给了accelerate方法。所以在方法内部self.speed指的就是my_car.speed。一个常见的坑忘记写self。如果你在方法内部访问属性或调用其他方法时没加self.Python会把它当成一个局部变量或全局函数从而报错。class WrongCar: def __init__(self, brand): brand brand # 错误这只是在修改局部变量brand没有给对象赋值属性 car WrongCar(“Tesla”) print(car.brand) # 报错AttributeError: ‘WrongCar’ object has no attribute ‘brand’正确的写法是self.brand brand。3. 面向对象三大特性实战理解了基本概念我们来看看支撑OOP大厦的三大支柱封装、继承和多态。我们通过一个更复杂的例子来串联它们。3.1 封装隐藏细节暴露接口封装有两个层面将数据属性和操作数据的方法方法捆绑在一起形成一个独立的单元类。我们上面一直在做的就是这件事。访问控制限制对对象内部状态的直接访问只通过公共方法来修改。这能保护数据不被意外破坏也使得类的内部实现可以自由更改而不影响外部代码。Python没有像Java那样的private、protected关键字来实现严格的访问控制它靠的是一种约定俗成的“命名约定”。公开的普通命名如speed,accelerate()。受保护的以一个下划线开头如_engine_status。这是一种提示意思是“这个属性或方法是为内部使用准备的外部代码最好不要直接访问但你要硬访问我也拦不住”。私有的以两个下划线开头如__secret_key。Python会对这个名字进行“名称改写”使得在类外部很难直接访问主要用于避免子类意外重写父类属性。class BankAccount: 一个模拟银行账户的类演示封装 def __init__(self, owner, initial_balance0): self.owner owner # 公开属性账户持有人 self._account_number “123456789” # 受保护属性提示内部使用 self.__balance initial_balance # 私有属性余额不希望被直接修改 # 公共方法存款是修改余额的“安全通道” def deposit(self, amount): if amount 0: self.__balance amount print(f“{self.owner} 存入 {amount}元。当前余额{self.__balance}元”) else: print(“存款金额必须大于0”) # 公共方法取款 def withdraw(self, amount): if 0 amount self.__balance: self.__balance - amount print(f“{self.owner} 取出 {amount}元。当前余额{self.__balance}元”) return amount else: print(“取款金额无效或余额不足”) return 0 # 公共方法查询余额而不是直接暴露__balance def get_balance(self): return self.__balance # 使用 account BankAccount(“张三”, 1000) account.deposit(500) # 正确方式通过公共接口操作 print(account.owner) # 可以访问公开属性 print(account._account_number) # 可以访问但不建议约定 # print(account.__balance) # 报错AttributeError: ‘BankAccount’ object has no attribute ‘__balance’ print(account.get_balance()) # 正确方式通过方法获取实操心得即使Python的“私有”并非绝对安全养成使用单下划线_和双下划线__的习惯也至关重要。这就像在代码里贴标签告诉你的队友以及未来的自己哪些是内部零件不要乱碰哪些是安全按钮可以随便按。这能极大减少团队协作中的bug。3.2 继承代码复用的利器想象一下你要开发一个游戏里面有多种角色战士、法师、射手。它们都有一些共同点生命值、魔法值、位置坐标以及移动、攻击等行为。如果每个角色类都从头写一遍代码会大量重复。这时就需要继承。继承允许我们定义一个父类基类来包含通用的属性和方法然后定义子类派生类来继承父类并添加自己特有的属性和方法或者重写父类的方法。class GameCharacter: 游戏角色父类 def __init__(self, name, hp, mp): self.name name self.hp hp # 生命值 self.mp mp # 魔法值 self.x 0 self.y 0 def move(self, dx, dy): self.x dx self.y dy print(f“{self.name} 移动到了 ({self.x}, {self.y})”) def attack(self): print(f“{self.name} 发动了普通攻击”) def show_status(self): print(f“{self.name} - HP: {self.hp}, MP: {self.mp}”) # 子类战士继承自GameCharacter class Warrior(GameCharacter): # 括号里写上父类名 def __init__(self, name, hp, mp, strength): # 1. 首先调用父类的__init__方法初始化共有属性 super().__init__(name, hp, mp) # super()代表父类这是一种标准写法 # 2. 然后初始化子类特有的属性 self.strength strength # 力量 # 子类可以重写父类方法 def attack(self): # 战士的普通攻击附带力量加成 damage 10 self.strength * 0.5 print(f“{self.name} 挥舞长剑造成 {damage} 点物理伤害。”) self.mp - 5 # 假设攻击消耗5点MP # 子类可以定义自己特有的方法 def shield_bash(self): if self.mp 15: print(f“{self.name} 发动盾击眩晕敌人。”) self.mp - 15 else: print(“魔法值不足无法发动盾击。”) # 子类法师 class Mage(GameCharacter): def __init__(self, name, hp, mp, intelligence): super().__init__(name, hp, mp) self.intelligence intelligence # 智力 def attack(self): # 法师的普通攻击是魔法飞弹 damage 5 self.intelligence * 1.2 print(f“{self.name} 发射魔法飞弹造成 {damage} 点魔法伤害。”) def fireball(self): if self.mp 30: damage 20 self.intelligence * 2.5 print(f“{self.name} 吟唱咒语释放火球术造成 {damage} 点范围魔法伤害”) self.mp - 30 else: print(“魔法值不足无法释放火球术。”) # 使用 arthas Warrior(“阿尔萨斯”, hp200, mp50, strength30) jaina Mage(“吉安娜”, hp120, mp150, intelligence40) arthas.show_status() # 继承自父类的方法 jaina.show_status() arthas.move(5, 0) # 继承自父类的方法 arthas.attack() # 调用的是子类重写后的方法 arthas.shield_bash() # 子类特有的方法 jaina.attack() jaina.fireball()继承的核心优势代码复用Warrior和Mage不需要重复定义name、hp、move等方法。逻辑清晰角色之间的共性在父类中一目了然差异性在子类中分别体现。易于扩展如果要加一个新角色“牧师”只需要创建一个继承GameCharacter的Priest类添加治疗技能即可。注意事项使用super().__init__()调用父类构造方法是Python3的标准做法它确保了父类被正确初始化。在多重继承中super()还能很好地处理方法解析顺序MRO问题。3.3 多态同一接口不同表现多态是继承的自然结果。它指的是不同类型的对象对同一消息方法调用会做出不同的响应。简单说就是“父类引用指向子类对象调用同名方法实际执行的是子类的方法”。在上面的例子中attack()方法就体现了多态。GameCharacter定义了attack的接口Warrior和Mage分别用不同的方式实现了它。def character_attack(character): 一个函数接收一个GameCharacter类型的参数 character.attack() # 这里并不关心character具体是战士还是法师 # 创建一个战士和一个法师对象列表 characters [Warrior(“格罗姆”, 180, 40, 35), Mage(“凯尔萨斯”, 100, 200, 50)] # 遍历列表让每个角色攻击 for char in characters: character_attack(char) # 输出 # 格罗姆 挥舞长剑造成 27.5 点物理伤害。 # 凯尔萨斯 发射魔法飞弹造成 65.0 点魔法伤害。函数character_attack的参数character类型是GameCharacter但它可以接受任何GameCharacter的子类对象。当调用character.attack()时程序会自动判断character实际是哪种类型并调用对应子类的attack方法。这就是多态的魅力代码针对抽象父类编写却能处理具体子类的情况使得程序非常灵活和可扩展。4. 进阶特性与常用特殊方法掌握了三大特性Python的OOP还有一些非常实用的“语法糖”和特殊方法能让你的类用起来更顺手更像Python内置的类型。4.1 类属性与实例属性我们之前定义的self.xxx都是实例属性属于每个对象自己。类属性则属于类本身所有实例共享。class Player: # 类属性 game_version “1.0.0” player_count 0 # 用于统计创建了多少个玩家 def __init__(self, name): # 实例属性 self.name name # 每创建一个玩家类属性player_count加1 Player.player_count 1 # 通过类名访问类属性 classmethod def show_version(cls): # cls 代表类本身 print(f“当前游戏版本{cls.game_version}”) # 访问类属性 print(Player.game_version) # 输出1.0.0 Player.show_version() # 输出当前游戏版本1.0.0 # 实例也可以访问类属性但通常不建议通过实例修改类属性 p1 Player(“Alice”) p2 Player(“Bob”) print(p1.game_version) # 输出1.0.0 print(Player.player_count) # 输出2关键区别修改self.game_version只会影响当前实例。修改Player.game_version会影响所有实例和类本身。类属性常用于定义常量、配置或统计信息。4.2 类方法与静态方法类方法使用classmethod装饰器第一个参数是cls类本身。常用于操作类属性或者创建类的替代构造器。静态方法使用staticmethod装饰器没有self或cls参数。它就像定义在类命名空间里的一个普通函数与类或实例没有直接数据交互只是逻辑上属于这个类。class DateUtils: staticmethod def is_valid_date(date_str): 静态方法简单验证日期字符串格式是否为YYYY-MM-DD import re pattern r“^\d{4}-\d{2}-\d{2}$” return bool(re.match(pattern, date_str)) classmethod def from_string(cls, date_str): 类方法作为替代构造器从字符串创建Date对象假设有Date类 if cls.is_valid_date(date_str): year, month, day map(int, date_str.split(‘-’)) # 这里假设返回一个Date实例这里简化处理 return {“year”: year, “month”: month, “day”: day} else: raise ValueError(“Invalid date format”) # 使用静态方法无需创建对象 print(DateUtils.is_valid_date(“2023-10-27”)) # True print(DateUtils.is_valid_date(“23-10-27”)) # False # 使用类方法 date_obj DateUtils.from_string(“2023-10-27”) print(date_obj) # {‘year’: 2023, ‘month’: 10, ‘day’: 27}4.3 常用特殊方法魔术方法特殊方法以双下划线开头和结尾如__init__。Python会在特定时机自动调用它们让你的自定义对象表现得像内置类型。__str__和__repr__定义对象的字符串表示形式。__str__面向用户print(obj)或str(obj)时调用。要求可读性好。__repr__面向开发者在交互式环境直接输入obj时调用或repr(obj)。要求明确 ideally should look like a valid Python expression that could be used to recreate the object.class Book: def __init__(self, title, author): self.title title self.author author def __str__(self): return f“《{self.title}》 - {self.author}” def __repr__(self): return f“Book(‘{self.title}’, ‘{self.author}’)” book Book(“Python编程从入门到实践”, “Eric Matthes”) print(book) # 输出《Python编程从入门到实践》 - Eric Matthes (调用 __str__) print(repr(book)) # 输出Book(‘Python编程从入门到实践’, ‘Eric Matthes’) (调用 __repr__)__len__定义len(obj)的行为。__getitem__和__setitem__定义索引操作obj[key]和赋值操作obj[key] value的行为让你的对象可以像列表或字典一样使用。class Bookshelf: def __init__(self): self.books [] def add_book(self, book): self.books.append(book) def __len__(self): return len(self.books) def __getitem__(self, index): return self.books[index] shelf Bookshelf() shelf.add_book(Book(“Book1”, “Author1”)) shelf.add_book(Book(“Book2”, “Author2”)) print(len(shelf)) # 输出2 (调用 __len__) print(shelf[1]) # 输出《Book2》 - Author2 (调用 __getitem__)__call__让对象实例可以像函数一样被调用。class Multiplier: def __init__(self, factor): self.factor factor def __call__(self, x): return x * self.factor double Multiplier(2) print(double(5)) # 输出10。 实例double被像函数一样调用熟练运用这些特殊方法能极大提升你自定义类的易用性和表达力。5. 综合案例设计一个简单的图书馆管理系统让我们把所有知识串联起来设计一个迷你版的图书馆管理系统。这个案例会涉及多个类之间的交互。5.1 系统分析与类设计我们需要管理书、读者以及借阅记录。这自然对应三个核心类Book类代表一本书。属性书号、书名、作者、是否被借出。Reader类代表一位读者。属性读者ID、姓名、已借阅书籍列表。Library类代表图书馆是系统的核心管理类。属性所有图书的目录、所有读者的注册信息。方法添加图书、注册读者、借书、还书、查询等。5.2 代码实现class Book: 图书类 def __init__(self, book_id, title, author): self.book_id book_id self.title title self.author author self.is_borrowed False # 初始状态为未借出 def __str__(self): status “已借出” if self.is_borrowed else “在馆” return f“ID: {self.book_id} | 《{self.title}》 | {self.author} | 状态: {status}” class Reader: 读者类 def __init__(self, reader_id, name): self.reader_id reader_id self.name name self.borrowed_books [] # 存储当前借阅的Book对象列表 def __str__(self): return f“读者ID: {self.reader_id} | 姓名: {self.name} | 借阅数量: {len(self.borrowed_books)}” def can_borrow(self): 检查读者是否还能借书假设每人最多借5本 return len(self.borrowed_books) 5 def borrow_book(self, book): 读者借一本书 if self.can_borrow() and not book.is_borrowed: self.borrowed_books.append(book) book.is_borrowed True print(f“{self.name} 成功借阅《{book.title}》。”) return True else: reason “借阅数量已达上限” if not self.can_borrow() else “该书已被借出” print(f“{self.name} 借阅《{book.title}》失败。原因{reason}”) return False def return_book(self, book): 读者还一本书 if book in self.borrowed_books: self.borrowed_books.remove(book) book.is_borrowed False print(f“{self.name} 已归还《{book.title}》。”) return True else: print(f“错误{self.name} 并未借阅《{book.title}》。”) return False class Library: 图书馆管理类 def __init__(self): self.books {} # 字典key为book_idvalue为Book对象 self.readers {} # 字典key为reader_idvalue为Reader对象 def add_book(self, book_id, title, author): 添加新书到馆藏 if book_id not in self.books: new_book Book(book_id, title, author) self.books[book_id] new_book print(f“成功添加新书《{title}》”) else: print(f“添加失败书号 {book_id} 已存在。”) def register_reader(self, reader_id, name): 注册新读者 if reader_id not in self.readers: new_reader Reader(reader_id, name) self.readers[reader_id] new_reader print(f“成功注册读者{name} (ID: {reader_id})”) else: print(f“注册失败读者ID {reader_id} 已存在。”) def borrow_book(self, reader_id, book_id): 处理借书流程 reader self.readers.get(reader_id) book self.books.get(book_id) if not reader: print(f“借书失败未找到读者ID {reader_id}。”) return False if not book: print(f“借书失败未找到图书ID {book_id}。”) return False # 委托给Reader对象的borrow_book方法处理 return reader.borrow_book(book) def return_book(self, reader_id, book_id): 处理还书流程 reader self.readers.get(reader_id) book self.books.get(book_id) if not reader: print(f“还书失败未找到读者ID {reader_id}。”) return False if not book: print(f“还书失败未找到图书ID {book_id}。”) return False # 委托给Reader对象的return_book方法处理 return reader.return_book(book) def search_book(self, keyword): 根据关键词书名/作者搜索图书 results [] keyword keyword.lower() for book in self.books.values(): if keyword in book.title.lower() or keyword in book.author.lower(): results.append(book) return results def show_status(self): 显示图书馆概况 print(“\n 图书馆当前状态 ”) print(f“馆藏图书总数{len(self.books)}”) print(f“注册读者总数{len(self.readers)}”) borrowed_count sum(1 for book in self.books.values() if book.is_borrowed) print(f“已借出图书数{borrowed_count}”)5.3 系统运行演示# 初始化图书馆 lib Library() # 1. 添加图书 lib.add_book(“B001”, “Python编程从入门到实践”, “Eric Matthes”) lib.add_book(“B002”, “流畅的Python”, “Luciano Ramalho”) lib.add_book(“B003”, “算法图解”, “Aditya Bhargava”) # 2. 注册读者 lib.register_reader(“R001”, “张三”) lib.register_reader(“R002”, “李四”) # 3. 借书操作 print(“\n--- 借书操作 ---”) lib.borrow_book(“R001”, “B001”) # 张三借Python入门 lib.borrow_book(“R001”, “B002”) # 张三借流畅的Python lib.borrow_book(“R002”, “B001”) # 李四想借Python入门应失败已被借走 # 4. 搜索图书 print(“\n--- 搜索图书 ---”) found_books lib.search_book(“python”) for book in found_books: print(book) # 5. 还书操作 print(“\n--- 还书操作 ---”) lib.return_book(“R001”, “B001”) # 6. 再次尝试借阅 print(“\n--- 再次借阅 ---”) lib.borrow_book(“R002”, “B001”) # 李四现在可以借了 # 7. 查看状态 lib.show_status()这个案例虽然简单但完整展示了OOP的思想将数据和对数据的操作封装在对象内部对象之间通过发送消息调用方法进行协作。Library类作为管理中枢协调Book和Reader对象完成各种业务逻辑。如果你需要增加新功能比如图书分类、借阅期限、逾期罚款只需要在相应的类中添加属性和方法即可代码结构清晰易于维护。6. 常见问题与避坑指南在实际使用OOP时新手常会遇到一些困惑和陷阱。这里总结几个高频问题。6.1 何时该使用类何时用函数就够了这是一个设计哲学问题。一个简单的判断原则是如果你要处理的数据是一组紧密相关的属性并且有多个函数专门操作这组数据那么就应该考虑封装成一个类。适合用函数工具函数如计算平均值、格式化字符串、读写文件。它们输入明确输出明确不依赖于复杂的外部状态。适合用类需要维护内部状态的实体如游戏角色、银行账户、订单。逻辑上是一个“东西”或“概念”并且有多个行为方法如“网络连接器”、“日志记录器”、“配置管理器”。当你发现有一堆全局变量和函数搅在一起难以管理时就是重构为类的好时机。6.2 继承的深度与“组合优于继承”继承虽然强大但过度使用会导致类层次结构过于复杂、僵化“脆弱的基类”问题。现代软件设计更推崇“组合优于继承”的原则。组合将一个类的对象作为另一个类的属性。比如Car类有一个Engine类的对象作为其engine属性而不是让Car继承Engine。何时用继承当子类确实是父类的一种特殊类型并且存在“是一个”的关系时如Dogis anAnimal。何时用组合当类之间是“有一个”或“用到一个”的关系时如Carhas anEngine,Libraryuses aDatabaseConnector。# 组合示例 class Engine: def start(self): print(“引擎启动...”) class Car: def __init__(self): self.engine Engine() # 组合Car有一个Engine def start(self): self.engine.start() print(“汽车已启动。”) my_car Car() my_car.start()组合提供了更大的灵活性你可以轻易地更换不同的Engine而不需要修改Car类的继承树。6.3__init__不是构造器一个常见的误解是把__init__叫做构造器。严格来说Python中真正的构造器是__new__方法它负责创建并返回一个新的对象实例。而__init__是在__new__之后被调用的负责初始化这个新创建的对象。对于绝大多数日常使用你只需要定义__init__来设置初始状态即可__new__主要用于控制不可变类型如元组、字符串的创建或实现单例模式等高级场景。6.4 循环导入问题当两个模块文件中的类需要相互引用时可能会产生循环导入错误。# file_a.py from file_b import ClassB class ClassA: def __init__(self): self.b ClassB() # file_b.py from file_a import ClassA # 错误此时file_a可能还没完全加载ClassA class ClassB: def __init__(self): self.a ClassA()解决方案重构代码检查是否真的需要双向紧密耦合尝试将公共部分提取到第三个模块。局部导入在方法内部需要时才导入。# file_b.py class ClassB: def __init__(self): from file_a import ClassA # 在方法内部导入 self.a ClassA()使用类型提示字符串Python 3.7# file_b.py from typing import TYPE_CHECKING if TYPE_CHECKING: from file_a import ClassA class ClassB: def __init__(self): self.a: ‘ClassA’ None # 使用字符串注解 def set_a(self, a_obj: ‘ClassA’): self.a a_obj面向对象编程是一种强大的编程范式它初学时有门槛但一旦掌握将成为你构建复杂、可维护程序的核心工具。从今天起试着用“对象”的视角去看待你要解决的问题思考哪些数据和操作可以捆绑在一起如何通过消息传递让对象协作。多写、多重构你会逐渐体会到OOP带来的结构之美。