Python面向对象编程:从类与对象到三大特性与实战技巧

📅 2026/8/14 5:21:14
Python面向对象编程:从类与对象到三大特性与实战技巧
1. 项目概述为什么面向对象是Python编程的基石如果你是从Python的print(“Hello World”)开始一路学到列表、字典和函数那么恭喜你你已经走过了新手村。但当你开始尝试写一个稍微复杂点的程序比如一个简单的学生管理系统或者一个游戏里的角色时你可能会发现用一堆零散的变量和函数来管理数据代码很快就会变得混乱不堪像一团理不清的毛线。这时候你就需要“面向对象编程”这个强大的工具箱了。简单来说面向对象编程是一种编程范式它把程序看作是一系列“对象”的集合。每个对象都是一个独立的实体它既有自己的属性在Python里叫“属性”或“成员变量”也有自己的行为在Python里叫“方法”。这非常符合我们认识现实世界的方式。比如一只“狗”是一个对象它有“品种”、“年龄”、“名字”等属性也有“叫”、“跑”、“吃”等方法。这种思维方式能让你的代码结构更清晰、更易维护、也更易于复用。对于Python开发者而言面向对象不是选修课而是必修课。无论是使用Django构建Web后端用Pygame写个小游戏还是用Pandas做数据分析底层都大量运用了类和对象。不理解面向对象你读很多优秀的第三方库源码时会一头雾水自己写出的代码也难以达到工程化的水准。所以今天我们就来彻底拆解Python中的面向对象编程从最基础的类与对象到封装、继承、多态三大特性再到那些让代码更优雅的高级语法和实战技巧。我会结合我这些年踩过的坑和总结的经验让你不仅能理解概念更能写出地道的、Pythonic的面向对象代码。2. 核心概念拆解类、对象与实例化2.1 类对象的蓝图你可以把“类”想象成一个产品的设计图纸。比如“智能手机”的设计图。这张图纸上定义了智能手机应该有哪些组成部分属性屏幕尺寸、电池容量、处理器型号也定义了它能做什么方法打电话、拍照、上网。在Python中我们用class关键字来定义这个“蓝图”。类名通常采用大驼峰命名法每个单词首字母大写。class SmartPhone: 智能手机的类定义 pass这只是一个最简单的类里面什么都没有。pass是一个占位符表示“这里暂时什么也不做”。现在这个SmartPhone类就像一张白纸我们需要为它添加上属性和方法。2.2 对象图纸的实物有了设计图类我们就可以根据它来生产出实实在在的产品这个产品就是“对象”也叫“实例”。这个过程叫做“实例化”。# 根据SmartPhone类创建两个具体的手机对象 my_phone SmartPhone() # 我的手机 your_phone SmartPhone() # 你的手机 print(my_phone) # 输出类似__main__.SmartPhone object at 0x0000012345ABCDEF print(your_phone) # 输出另一个内存地址my_phone和your_phone就是两个独立的对象。虽然它们都源自同一个类SmartPhone但它们是两个完全不同的实体占据不同的内存空间。print打印出的那一串包含内存地址的信息就是它们在计算机世界里的“身份证”。2.3__init__方法对象的初始化一张空白的设计图没什么用。我们通常会在图纸上标明产品的初始配置。在Python类中这个工作由__init__方法来完成。这是一个特殊的方法以双下划线开头和结尾在创建对象时会自动调用。它的核心作用是为新创建的对象设置初始属性。class SmartPhone: def __init__(self, brand, model, screen_size): 初始化方法 Args: brand (str): 品牌 model (str): 型号 screen_size (float): 屏幕尺寸英寸 self.brand brand # 将传入的brand参数赋值给当前对象的brand属性 self.model model # 将传入的model参数赋值给当前对象的model属性 self.screen_size screen_size self.battery 100 # 可以设置一个默认值比如新手机电量100% print(f一部新的{self.brand} {self.model}手机已创建。) # 实例化对象并传入初始化参数 phone1 SmartPhone(华为, Mate 60, 6.8) phone2 SmartPhone(苹果, iPhone 15, 6.1)这里有几个关键点self参数这是类中所有实例方法的第一个参数它代表“对象自身”。在方法内部我们通过self.属性名来访问或设置这个对象特有的属性。在调用方法时Python会自动传入这个参数我们不需要手动传递。初始化属性__init__方法里我们利用传入的参数brand,model,screen_size为对象的属性赋值。这样每个对象一“出生”就有了自己独特的状态。默认属性像self.battery 100这样我们可以设置一些不依赖外部参数的默认属性。现在phone1和phone2就有了各自不同的属性值。我们可以通过点号.来访问它们print(phone1.brand) # 输出华为 print(phone2.screen_size) # 输出6.1注意__init__并不是创建对象的方法真正创建对象的是__new__方法。但__new__在绝大多数情况下我们不需要自己定义__init__是在对象被__new__创建之后用于初始化对象的。你可以简单理解为__new__负责“生”__init__负责“养”初始化。3. 面向对象三大特性深度解析理解了类和对象我们就有了材料。接下来需要学习如何用这些材料构建坚固、灵活、可扩展的程序大厦。这就是面向对象的三大核心特性封装、继承和多态。3.1 封装隐藏细节暴露接口封装是面向对象的第一原则。它的核心思想是将数据属性和操作数据的方法函数捆绑在一起形成一个独立的单元即对象同时对外部隐藏对象内部的实现细节仅通过有限的公共接口与外部交互。为什么要封装安全性防止外部代码随意修改对象内部的重要数据。易用性使用者无需关心内部复杂的实现逻辑只需调用简单的接口。可维护性内部实现的修改只要不改变对外接口就不会影响外部调用者。在Python中封装主要通过“访问控制”来实现。但这和Java、C的private、public关键字不同Python采用了一种叫做“名称改写”的约定俗成机制。公有属性和方法直接定义的属性和方法如self.branddef call()。外部可以直接访问。受保护的属性和方法以一个下划线开头如_internal_data。这是一个约定意思是“虽然你能访问但请把它视为非公开的不要直接使用”。这主要靠程序员自觉。私有属性和方法以两个下划线开头如__secret_key。Python会对这样的名称进行“名称改写”使其在类外部难以直接访问从而实现一定程度的私有化。class BankAccount: 银行账户类演示封装 def __init__(self, owner, initial_balance): self.owner owner # 公有属性账户持有人 self._bank_code ICBC # 受保护属性银行代码约定 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 def get_balance(self): 提供一个公共方法来查询余额接口 return self.__balance # 使用 account BankAccount(张三, 1000) account.deposit(500) # 通过公共接口操作 account.withdraw(200) # 通过公共接口操作 print(account.owner) # 可以直接访问公有属性 print(account.get_balance()) # 必须通过接口访问私有属性 # 尝试直接访问私有属性不推荐且会被改写 # print(account.__balance) # 报错AttributeError # 但Python的“私有”是伪私有通过改写后的名字仍可访问不推荐 # print(account._BankAccount__balance) # 可以但强烈不建议这么做实操心得在实际项目中我强烈建议使用属性装饰器property来管理私有属性的访问这比直接定义get_balance、set_balance方法更Pythonic。它让你能以访问属性的语法来调用方法实现了更优雅的封装。class BankAccountPro: def __init__(self, owner, initial_balance): self.owner owner self.__balance initial_balance property def balance(self): 获取余额只读 print(查询了余额) return self.__balance balance.setter def balance(self, value): 设置余额有逻辑控制 if value 0: print(余额不能为负) else: self.__balance value print(f余额已更新为{value}) account_pro BankAccountPro(李四, 2000) print(account_pro.balance) # 像属性一样访问实际调用了getter方法 account_pro.balance 2500 # 像属性一样赋值实际调用了setter方法 account_pro.balance -100 # setter方法内的逻辑会拦截非法赋值3.2 继承代码复用的利器继承允许我们定义一个类子类/派生类来继承另一个类父类/基类的属性和方法。子类会自动拥有父类的功能同时可以添加自己特有的功能或者修改父类的功能。这实现了代码的复用和层次化分类。比如“智能手机”是一个大类“游戏手机”和“拍照手机”是它的子类。游戏手机继承了智能手机的所有功能能打电话、上网但强化了散热和性能拍照手机则强化了摄像头模块。class SmartPhone: 父类智能手机 def __init__(self, brand, model): self.brand brand self.model model self.is_on False def power_on(self): self.is_on True print(f{self.brand} {self.model} 已开机。) def make_call(self, number): if self.is_on: print(f正在拨打 {number}...) else: print(请先开机。) class GamingPhone(SmartPhone): # 继承自SmartPhone 子类游戏手机 def __init__(self, brand, model, cooling_system): # 1. 首先调用父类的初始化方法 super().__init__(brand, model) # 等同于 SmartPhone.__init__(self, brand, model) # 2. 然后初始化子类特有的属性 self.cooling_system cooling_system self.game_mode False def enable_game_mode(self): 子类特有的方法 if self.is_on: self.game_mode True print(游戏模式已开启性能全开) else: print(请先开机。) # 使用 rog_phone GamingPhone(华硕, ROG Phone 7, 液冷散热) rog_phone.power_on() # 调用继承自父类的方法 rog_phone.make_call(10086) # 调用继承自父类的方法 rog_phone.enable_game_mode() # 调用子类特有的方法关键点解析super()函数在子类中用于调用父类的方法。最常用的场景就是在子类的__init__中初始化父类的属性。使用super()是一种更通用、更好的方式尤其是在多继承中。方法重写如果子类定义了一个与父类同名的方法那么子类的方法会覆盖重写父类的方法。这是实现多态的基础。isinstance()和issubclass()这两个内置函数用于检查对象与类、类与类之间的关系。print(isinstance(rog_phone, GamingPhone)) # True print(isinstance(rog_phone, SmartPhone)) # True子类对象也是父类类型 print(issubclass(GamingPhone, SmartPhone)) # True常见问题与排查问题子类重写__init__后父类的属性没有初始化。排查检查子类__init__方法中是否在第一行调用了super().__init__(...)。问题多重继承时方法调用混乱菱形继承问题。排查Python使用C3线性化算法MRO方法解析顺序来确定方法查找顺序。可以使用类名.__mro__或类名.mro()来查看。在设计时应尽量避免复杂的多重继承优先使用“组合”代替“继承”。3.3 多态同一接口不同表现多态的字面意思是“多种形态”。在OOP中它指的是不同类型的对象对同一消息方法调用做出不同的响应。多态依赖于继承和方法重写。一个经典的例子是几何图形计算面积。不同的图形圆、矩形、三角形都有“计算面积”这个行为但计算公式完全不同。class Shape: 形状基类抽象类 def area(self): 计算面积。基类中不实现具体逻辑由子类重写。 raise NotImplementedError(子类必须实现area方法) # 强制子类重写 class Circle(Shape): def __init__(self, radius): self.radius radius def area(self): import math return math.pi * self.radius ** 2 class Rectangle(Shape): def __init__(self, width, height): self.width width self.height height def area(self): return self.width * self.height # 多态的体现 def print_area(shape_obj): 这个函数接收一个Shape类型的对象或其子类对象 # 我们不关心传入的具体是Circle还是Rectangle只关心它有area方法 print(f该图形的面积是{shape_obj.area():.2f}) # 创建不同的图形对象 circle Circle(5) rectangle Rectangle(4, 6) # 调用同一个函数传入不同类型的对象得到不同的行为结果 print_area(circle) # 输出该图形的面积是78.54 print_area(rectangle) # 输出该图形的面积是24.00print_area函数的设计体现了多态的精髓它只依赖于Shape这个抽象接口约定要有area方法而不依赖于具体的Circle或Rectangle类。这意味着未来我们新增一个Triangle类只要它继承了Shape并实现了area方法print_area函数就可以不加修改地处理它。这极大地提高了代码的扩展性和可维护性。实操心得在Python中我们通常不严格定义抽象基类ABC而是使用“鸭子类型”。鸭子类型是动态语言多态的一种更灵活的实现方式“如果它走起路来像鸭子叫起来也像鸭子那么它就是鸭子。” 也就是说我们不关心对象的类型本身只关心它是否拥有我们需要的方法或属性。# 鸭子类型示例不需要显式继承Shape class Triangle: # 没有继承任何类 def __init__(self, base, height): self.base base self.height height def area(self): # 但拥有area方法 return 0.5 * self.base * self.height triangle Triangle(3, 4) print_area(triangle) # 依然可以正常工作因为triangle有area方法。Python的len()、iter()等内置函数都是基于鸭子类型工作的。这使得Python的代码非常灵活。4. 类与实例的进阶机制4.1 类属性与实例属性这是初学者最容易混淆的点之一。实例属性属于单个对象实例。通过self.属性名 值在__init__或其他实例方法中定义。每个对象的实例属性值都是独立的。类属性属于类本身。在类内部、方法外部直接定义。所有实例共享同一个类属性。class Player: game_name 人狗大作战 # 类属性所有玩家玩的都是同一个游戏 def __init__(self, name): self.name name # 实例属性每个玩家有自己的名字 self.hp 100 # 实例属性每个玩家有自己的血量 # 访问类属性 print(Player.game_name) # 通过类名访问 player1 Player(张三) print(player1.game_name) # 通过实例访问实际上访问的是类的game_name # 修改类属性会影响所有实例 Player.game_name 新的人狗大作战2024 print(player1.game_name) # 输出新的人狗大作战2024 player2 Player(李四) print(player2.game_name) # 输出新的人狗大作战2024 # 修改实例属性只影响该实例 player1.hp 80 print(player1.hp) # 80 print(player2.hp) # 100不受影响 # 注意通过实例修改类属性实际上会创建一个同名的实例属性 player1.game_name 我的自定义游戏 print(player1.game_name) # 输出我的自定义游戏这是实例属性 print(Player.game_name) # 输出新的人狗大作战2024类属性未变 print(player2.game_name) # 输出新的人狗大作战2024其他实例仍访问类属性重要提示当通过实例访问一个属性时Python会先在实例的命名空间中查找如果找不到再去类的命名空间中查找。这就是为什么player1.game_name一开始能访问到类属性而赋值后则创建了自己的实例属性。4.2 类方法与静态方法除了普通的实例方法第一个参数是self类中还可以定义类方法和静态方法。类方法使用classmethod装饰器定义第一个参数是cls代表类本身。类方法不能访问实例属性但可以访问和修改类属性。常用于创建工厂方法根据不同的参数创建不同类型的实例。静态方法使用staticmethod装饰器定义没有self或cls参数。它就像一个定义在类命名空间里的普通函数与类和实例都没有强制绑定关系只是逻辑上属于这个类。常用于工具函数。class DateUtil: 日期工具类 date_format %Y-%m-%d # 类属性默认日期格式 def __init__(self, date_str): from datetime import datetime self.date datetime.strptime(date_str, self.date_format) classmethod def set_default_format(cls, new_format): 类方法修改默认日期格式 cls.date_format new_format print(f默认格式已更改为{new_format}) classmethod def from_timestamp(cls, timestamp): 类方法作为工厂方法从时间戳创建对象 from datetime import datetime date_str datetime.fromtimestamp(timestamp).strftime(cls.date_format) return cls(date_str) # 调用 cls(...) 相当于 DateUtil(...) staticmethod def is_leap_year(year): 静态方法判断是否为闰年与类状态无关 return (year % 4 0 and year % 100 ! 0) or (year % 400 0) # 使用类方法修改类状态 DateUtil.set_default_format(%d/%m/%Y) d1 DateUtil(25/12/2023) print(d1.date) # 使用类方法作为工厂 d2 DateUtil.from_timestamp(1672444800) # 2023-01-01 00:00:00的时间戳 print(d2.date) # 使用静态方法 print(DateUtil.is_leap_year(2024)) # True print(d1.is_leap_year(2023)) # False也可以通过实例调用选择指南需要访问或修改实例状态- 用实例方法。需要访问或修改类状态或需要作为工厂方法- 用类方法。方法逻辑与类和实例都无关只是一个工具函数 - 用静态方法。5. 特殊方法魔术方法让对象更智能Python中那些以双下划线开头和结尾的方法如__init__、__str__被称为特殊方法或魔术方法。它们赋予了类特殊的行为。合理使用它们可以让你的自定义对象像内置类型一样自然。5.1 对象表示__str__与__repr____str__面向用户返回一个易读的字符串。当使用print(obj)或str(obj)时被调用。__repr__面向开发者返回一个无歧义的、尽可能完整的字符串理想情况下应该能用于重新创建这个对象。当在交互式环境直接输入对象名或使用repr(obj)时被调用。class Point: def __init__(self, x, y): self.x x self.y y def __str__(self): return fPoint({self.x}, {self.y}) def __repr__(self): return fPoint(x{self.x}, y{self.y}) p Point(3, 4) print(p) # 输出Point(3, 4) 调用 __str__ print(str(p)) # 输出Point(3, 4) 调用 __str__ print(repr(p)) # 输出Point(x3, y4) 调用 __repr__ # 在交互式环境中输入 p 会显示Point(x3, y4)实操心得一个好的习惯是至少实现__repr__因为如果__str__未定义Python会使用__repr__作为备选。__repr__的返回值最好能像eval(repr(obj))那样重新创建对象。5.2 让对象可比较、可哈希__eq__,__lt__,__hash__默认情况下自定义类的实例比较的是内存地址is操作符的行为。通过重写__eq__等于、__lt__小于等方法可以定义对象之间的比较逻辑。class Student: def __init__(self, name, score): self.name name self.score score def __eq__(self, other): 定义 操作 if isinstance(other, Student): return self.score other.score # 假设按分数比较 return NotImplemented def __lt__(self, other): 定义 操作 if isinstance(other, Student): return self.score other.score return NotImplemented # 定义了 __lt__ 后可以使用 sorted() 和 list.sort() # 如果需要其他比较操作符, , 可以使用 functools.total_ordering 装饰器简化 s1 Student(Alice, 90) s2 Student(Bob, 85) s3 Student(Charlie, 90) print(s1 s3) # True分数相同 print(s1 s2) # True因为定义了 __lt__Python能推导出 print(sorted([s1, s2, s3])) # 按分数排序[Bob(85), Alice(90), Charlie(90)]如果希望对象能作为字典的键或集合的元素必须实现__hash__方法返回一个整数并且要保证如果__eq__判断为相等的两个对象它们的__hash__值也必须相等。class StudentHashable(Student): def __hash__(self): # 通常使用用于比较的属性元组来生成哈希值 return hash((self.name, self.score)) sh1 StudentHashable(Alice, 90) sh2 StudentHashable(Alice, 90) print(sh1 sh2) # True print(hash(sh1) hash(sh2)) # True student_set {sh1, sh2} print(student_set) # 只有一个元素因为两者哈希相等且值相等5.3 让对象可调用__call__实现了__call__方法的对象可以像函数一样被调用。这在创建有状态的函数或实现装饰器类时非常有用。class Counter: def __init__(self): self.count 0 def __call__(self): self.count 1 print(f已被调用 {self.count} 次) return self.count counter Counter() counter() # 输出已被调用 1 次 counter() # 输出已被调用 2 次 result counter() # 输出已被调用 3 次 print(result) # 输出36. 面向对象设计原则与实战技巧理解了语法和特性如何设计出好的面向对象程序这里分享几个核心原则和实战技巧。6.1 组合优于继承继承虽然强大但过度使用会导致类层次结构复杂、脆弱父类的改动会影响所有子类。组合Composition是另一种复用代码的方式即一个类包含另一个类的实例作为其属性。场景对比假设我们设计游戏中的角色。继承思路Warrior战士、Mage法师都继承自Character角色。如果后来要增加一个“会飞的战士”你可能需要创建FlyingWarrior多重继承会变得复杂。组合思路Character类有一个ability能力属性这个属性可以是FightingAbility战斗能力、MagicAbility魔法能力、FlyingAbility飞行能力等类的实例。通过组合不同的能力对象可以灵活地创建各种角色而无需创建复杂的继承树。class FightingAbility: def attack(self): return 物理攻击 class MagicAbility: def cast_spell(self): return 火球术 class FlyingAbility: def fly(self): return 展翅高飞 class Character: def __init__(self, name): self.name name self.abilities [] # 组合持有能力对象的列表 def add_ability(self, ability): self.abilities.append(ability) def perform_actions(self): actions [] for ability in self.abilities: if isinstance(ability, FightingAbility): actions.append(ability.attack()) elif isinstance(ability, MagicAbility): actions.append(ability.cast_spell()) elif isinstance(ability, FlyingAbility): actions.append(ability.fly()) return actions # 创建一个会飞行和战斗的角色 hero Character(英雄) hero.add_ability(FlyingAbility()) hero.add_ability(FightingAbility()) print(hero.perform_actions()) # 输出[展翅高飞, 物理攻击]组合提供了更大的灵活性降低了类之间的耦合度。6.2 单一职责原则一个类应该只有一个引起它变化的原因。换句话说一个类只负责一项明确的职责。如果一个类承担了太多功能它就会变得难以理解、难以修改、难以复用。反面教材class BadReport: 一个糟糕的报表类负责了太多事情 def __init__(self, data): self.data data def fetch_data_from_database(self): ... # 职责1获取数据 def clean_data(self): ... # 职责2清洗数据 def analyze_data(self): ... # 职责3分析数据 def generate_pdf(self): ... # 职责4生成PDF def send_email(self): ... # 职责5发送邮件这个类一旦数据库接口、分析算法、PDF库或邮件服务发生变化都需要修改它。遵循单一职责class DataFetcher: def fetch(self): ... class DataCleaner: def clean(self, data): ... class DataAnalyzer: def analyze(self, data): ... class ReportGenerator: def generate_pdf(self, analysis_result): ... class NotificationService: def send_email(self, report_path): ... class ReportService: # 一个协调者组合各个单一职责的类 def __init__(self): self.fetcher DataFetcher() self.cleaner DataCleaner() self.analyzer DataAnalyzer() self.generator ReportGenerator() self.notifier NotificationService() def run_pipeline(self): data self.fetcher.fetch() clean_data self.cleaner.clean(data) result self.analyzer.analyze(clean_data) report_path self.generator.generate_pdf(result) self.notifier.send_email(report_path)这样每个类的职责都清晰单一易于测试和维护。6.3 使用属性Property进行数据验证前面提到用property实现优雅的封装。这里再强调其在数据验证中的作用。直接在__init__或普通setter方法里写验证逻辑是常见的做法但property让这更清晰。class Person: def __init__(self, name, age): self.name name self.age age # 这里会调用 age.setter property def age(self): return self._age # 实际数据存储在私有属性 _age 中 age.setter def age(self, value): if not isinstance(value, int): raise TypeError(年龄必须是整数) if not 0 value 150: raise ValueError(年龄必须在0到150之间) self._age value property def name(self): return self._name.title() # Getter中可以格式化数据 name.setter def name(self, value): if not value.strip(): raise ValueError(姓名不能为空) self._name value.strip() try: p Person( alice , 25) print(p.name, p.age) # 输出Alice 25 p.age 200 # 触发 ValueError except ValueError as e: print(e)这种方式保证了无论通过何种方式初始化、直接赋值修改属性验证逻辑都会生效。7. 常见问题与排查技巧实录在实际开发中面向对象编程会遇到各种“坑”。这里记录一些典型问题和解决方法。7.1 可变对象作为类属性的陷阱这是一个经典错误。将可变对象如列表、字典作为类属性会被所有实例共享。class ProblematicClass: shared_list [] # 类属性可变对象 def add_item(self, item): self.shared_list.append(item) # 这里修改的是类属性 obj1 ProblematicClass() obj2 ProblematicClass() obj1.add_item(A) obj2.add_item(B) print(obj1.shared_list) # 输出[A, B] print(obj2.shared_list) # 输出[A, B]两个实例“共享”了同一个列表 print(ProblematicClass.shared_list) # 输出[A, B]解决方案总是在__init__中初始化可变对象作为实例属性。class CorrectClass: def __init__(self): self.instance_list [] # 实例属性每个对象独立 def add_item(self, item): self.instance_list.append(item) obj1 CorrectClass() obj2 CorrectClass() obj1.add_item(A) obj2.add_item(B) print(obj1.instance_list) # 输出[A] print(obj2.instance_list) # 输出[B]7.2 在类方法中错误地使用实例属性类方法classmethod的第一个参数是cls代表类本身它无法访问self实例。试图在类方法中访问实例属性会导致错误。class MyClass: def __init__(self, value): self.value value classmethod def bad_method(cls): print(self.value) # 错误这里没有self classmethod def good_method(cls): print(cls.__name__) # 正确访问类属性排查如果遇到NameError: name self is not defined检查是否在类方法或静态方法中误用了self。7.3 多继承中的方法解析顺序MRO问题当使用多继承时如果多个父类有同名方法Python需要确定调用哪一个。它使用C3线性化算法可以通过类名.__mro__查看。class A: def do(self): print(A) class B(A): def do(self): print(B) super().do() # 调用MRO中的下一个 class C(A): def do(self): print(C) super().do() class D(B, C): def do(self): print(D) super().do() print(D.__mro__) # 输出(class __main__.D, class __main__.B, class __main__.C, class __main__.A, class object) d D() d.do() # 输出 # D # B # C # A调用d.do()时按照MRO顺序D - B - C - A执行。super()并不是简单地调用父类而是调用MRO中的下一个类。理解MRO对于设计复杂的类层次结构至关重要。避坑技巧尽量简化继承关系多用组合。如果必须多继承确保理解MRO并使用super()进行协作式方法调用。7.4__init__不是构造函数很多初学者误以为__init__是构造函数。实际上__new__才是负责创建并返回新实例对象的“构造函数”而__init__是初始化已创建实例的“初始化方法”。绝大多数情况下我们不需要重写__new__除非需要控制不可变类型如int,str,tuple的创建过程或者实现单例模式、对象池等高级模式。class Singleton: _instance None def __new__(cls, *args, **kwargs): if cls._instance is None: print(创建唯一实例) cls._instance super().__new__(cls) # 调用object的__new__创建实例 return cls._instance # 总是返回同一个实例 def __init__(self, value): print(f初始化实例值为{value}) self.value value s1 Singleton(第一次) s2 Singleton(第二次) print(s1 is s2) # True print(s1.value, s2.value) # 第二次 第二次 注意__init__每次都会被调用这个例子展示了单例模式但请注意__init__在每次实例化时都会被调用这可能导致属性被意外重置。更健壮的单例实现需要更精细的控制。面向对象编程是Python从脚本走向工程化的关键一步。它提供的不仅仅是一种语法更是一种组织代码、思考问题的范式。从理解self和__init__开始到熟练运用封装、继承、多态构建模块化程序再到利用特殊方法和设计原则写出Pythonic的优雅代码每一步都需要大量的练习和思考。我个人的体会是不要为了面向对象而面向对象简单的脚本用函数式可能更清晰。但当你的代码逻辑开始变得复杂数据和行为需要紧密绑定或者你需要构建可复用的组件时面向对象的设计思想就会成为你最得力的工具。最后一个小建议多读优秀的开源代码比如Django、Requests等库的源码看看大师们是如何运用面向对象来构建庞大而清晰的系统的这比任何教程都管用。