Python基础学习-13

Python基础学习-13 一、面向对象编程入门1、知识点为什么要面向对象面向对象编程OOP的核心思想是把数据和处理数据的方法打包在一起形成一个对象。就像现实世界中一辆车既有属性颜色、速度、油量又有行为加速、刹车、加油——属性和行为属于同一个实体。类Class蓝图/模具定义了一类事物的属性和行为模板。对象Object根据类创建出来的具体实例就像根据模具生产出来的实物。定义类与创建对象class类名:# 类的内容pass类名通常使用大驼峰命名法每个单词首字母大写如StudentCard一个类可以创建无数个对象每个对象拥有独立的属性值__init__构造方法__init__是类的初始化方法也称构造方法在创建对象时自动调用用于给对象的属性赋初始值。classDog:def__init__(self,name,age):self.namename# self.name 是对象的属性name 是传入的参数self.ageageself代表实例本身必须是第一个参数名字约定俗成用self通过self.属性名定义的属性可以在类的其他方法中访问实例方法实例方法是定义在类中的函数第一个参数必须是self通过它访问和操作对象的属性。classDog:def__init__(self,name):self.namenamedefbark(self):print(f{self.name}汪汪叫)调用方式对象.方法名()类属性 vs 实例属性类型定义位置归属共享性实例属性__init__中通过self.xxx定义每个对象独立持有互不影响类属性直接写在类体内方法外属于类本身所有实例共享classStudent:school清华附中# 类属性所有学生共享def__init__(self,name):self.namename# 实例属性每个学生有不同的名字2、代码示例银行账户classBankAccount:银行账户类def__init__(self,owner,balance0):初始化账户开户人、余额self.ownerowner self.balancebalancedefdeposit(self,amount):存款ifamount0:self.balanceamountprint(f存入{amount}元当前余额{self.balance})else:print(存款金额必须大于0)defwithdraw(self,amount):取款if0amountself.balance:self.balance-amountprint(f取出{amount}元当前余额{self.balance}元)else:print(余额不足或金额无效)defshow_balance(self):显示余额print(f{self.owner}的账户余额{self.balance}元)#创建对象并使用acc1BankAccount(小明,1000)acc1.show_balance()# 小明 的账户余额: 1000 元acc1.deposit(500)# 存入 500 元当前余额: 1500 元acc1.withdraw(300)# 取出 300 元当前余额: 1200 元acc2BankAccount(小红,2000)acc2.show_balance()# 小红 的账户余额: 2000 元与小明的账户互不影响学生管理系统classStudent:学生类管理学生信息与成绩#类属性所有学生使用同一套等级标准grade_standard{90:优秀,80:良好,60:及格,0:不及格}def__init__(self,name,student_id):初始化姓名学号空成绩列表self.namename self.student_idstudent_id self.score[]# 存储各科成绩的列表self.__password123456# 私有属性约定双下划线开头defadd_score(self,subject,score):添加一门课的成绩if0score100:self.score.append({科目:subject,分数:score})print(f{self.name}{subject}成绩{score}已录入)else:print(成绩应在0-100之间)defget_average(self):计算平均分数ifnotself.score:return0totalsum(item[分数]foriteminself.score)returntotal/len(self.score)defget_grade(self):根据平均分返回等级avgself.get_average()forthresholdinsorted(self.grade_standard.keys(),reverseTrue):ifavgthreshold:returnself.grade_standard[threshold]return未知defshow_report(self):打印成绩单print(f\n{self.name}({self.student_id}) 成绩单 )foriteminself.score:print(f{item[科目]}:{item[分数]})print(f 平均分:{self.get_average():.1f})print(f 等级:{self.get_grade()})print(*35)#测试stu1Student(张三,20260701)stu1.add_score(语文,85)stu1.add_score(数学,92)stu1.add_score(英语,78)stu1.show_report()stu2Student(李四,2024002)stu2.add_score(语文,95)stu2.add_score(数学,88)stu2.show_report()3、补充self不是关键字理论上可以换成任何名字如this但 Python 社区强烈约定使用self换名字会让你的代码被同行鄙视。私有属性的约定Python 没有真正的私有机制。_name单下划线表示受保护的__name双下划线会触发名称改写Name Mangling变成_ClassName__name是一种弱保护。面向对象设计原则速览单一职责一个类只负责一件事别让Student类又管成绩又管排课表组合优于继承能用有一个组合解决的别急着用是一个继承依赖倒置依赖抽象而非具体实现二、继承与多态1、知识点继承是什么继承**是一种is-a关系——子类是父类的一种特化版本。子类自动拥有父类的所有属性和方法同时可以添加新功能或修改已有行为。父类基类 ──继承── 子类派生类 Animal Dog ├── name ├── name继承 └── speak() ├── speak()重写 └── wag_tail()新增单继承一个子类只有一个直接父类结构清晰90% 的场景够用多继承一个子类同时继承多个父类Mixin 模式的核心基础核心概念对比表概念作用关键语法典型场景单继承子类复用父类代码class Dog(Animal)大多数继承需求方法重写子类提供自己的实现子类中定义同名方法定制父类行为super()调用父类的方法super().method()初始化继承链、扩展父类方法多继承组合多个父类能力class C(A, B)Mixin 模式、多重能力组合MRO方法解析顺序ClassName.__mro__理解多继承调用路径鸭子类型关注行为而非类型无需继承有方法即可松耦合设计、插件系统方法重写 vs super() 扩展方式代码效果完全重写def speak(self): print(汪汪)父类方法被覆盖不再调用扩展重写def speak(self): super().speak(); print(汪汪)先执行父类逻辑再追加子类逻辑鸭子类型 vs 传统继承维度继承多态鸭子类型关系要求必须有继承关系无需继承有方法即可类型检查isinstance(obj, Parent)不检查类型耦合度较高极低Python 哲学能用但不推荐滥用“如果它走路像鸭子叫起来像鸭子它就是鸭子”2、代码示例单继承方法重写# 单继承 方法重写# 演示子类继承父类属性并重写方法classAnimal:动物类def__init__(self,name):self.namename# 为示例设置名字属性defspeak(self):动物叫声通用returnf{self.name}发出声音definfo(self):展示信息returnf我是{self.name}classDog(Animal):狗类继承自动物defspeak(self):重写父类speak方法——提供狗的特化行为returnf{self.name}汪汪叫defwag_tail(self):狗独有的方法——父类没有returnf{self.name}摇尾巴~classCat(Animal):猫类继承自动物defspeak(self):# 每个子类根据自己的特点重写 speakreturnf{self.name}喵喵叫# 使用示例dogDog(旺财)# 创建 Dog 实例传入名字catCat(咪咪)# 创建 Cat 实例print(dog.info())# 调用继承自 Animal 的 info()——输出我是 旺财print(dog.speak())# 调用 Dog 重写的 speak()——输出旺财 汪汪叫print(dog.wag_tail())# 调用 Dog 独有的方法——输出旺财 摇尾巴~print(cat.info())# 调用继承自 Animal 的 info()——输出我是 咪咪print(cat.speak())# 调用 Cat 重写的 speak()——输出咪咪 喵喵叫super() 的使用场景#super()classPerson:人类基类def__init__(self,name,age):self.namename self.ageageprint(fPerson.__init__ 被调用{name},{age}岁)defintroduce(self):returnf我叫{self.name}今年{self.age}岁#场景1super().__init__() 初始化继承链classStudent(Person):def__init__(self,name,age,student_id):# super()调用父类 Person的__init__,传递name和agesuper().__init__(name,age)self.student_idstudent_id#子类独有属性defintroduce(self):# 场景二“扩展父类方法——先拿父类再追加自己的base_infosuper().introduce()returnf{base_info}学号{self.student_id}#场景三多继承中super()classTalker:说话能力 Mixindefspeak_message(self):return我能说话classRunner:跑步能力 Mixindefrun_message(self):return我能跑步classSuperStudent(Student,Talker,Runner):多继承的学生——同时拥有学生属性 说话 跑步能力def__init__(self,name,age,student_id):# super() 按 MRO 顺序调用下一个类的 __init__super().__init__(name,age,student_id)# --- 使用示例 ---stuStudent(小明,20,S2024001)print(stu.introduce())# 输出我叫 小明今年 20 岁学号 S2024001super_stuSuperStudent(小红,21,S2024002)print(super_stu.introduce())# 来自 Student - Personprint(super_stu.speak_message())# 来自 Talkerprint(super_stu.run_message())# 来自 Runner多继承 MRO# 2.3 多继承 和 MRO方法解析顺序classA:defmethod(self):print(A.method 被调用)# 关键即使 A 没有显式继承其他类# 在多继承场景下 super() 仍会按 MRO 链继续调用classB(A):defmethod(self):print(B.method 被调用)super().method()# 按 MRO 顺序找下一个classC(A):defmethod(self):print(C.method 被调用)super().method()# 按 MRO 顺序找下一个classD(B,C):多继承D 继承 B 和 Cdefmethod(self):print(D.method 被调用)super().method()# 按 MRO 顺序找下一个# --- 查看 MRO ---print(D 的 MRO 顺序:)forclsinD.__mro__:# __mro__ 返回方法解析顺序的元组print(f -{cls.__name__})print(\n调用 d.method():)dD()# 创建 D 实例d.method()# 观察调用链D - B - C - AC3 线性化规则Python 保证子类在父类前面且保持父类原有的声明顺序。这就是为什么D(B, C)中 B 在 C 之前。鸭子类型# 鸭子类型只要对象有需要的方法就能正常工作classDuck:鸭子类defquack(self):return嘎嘎嘎deffly(self):return鸭子飞起来了classPerson:人类并没有继承DCUKdefquack(self):return学鸭子叫deffly(self):return坐飞机飞classDog:狗只有quack没有flydefquack(self):return汪汪## 关键函数不检查类型只检查对象是否有需要的方法defmake_it_quack(thing):让任何能 quack 的对象发出叫声——鸭子类型的核心思想print(thing.quack())defmake_it_fly(thing):让任何能 fly 的对象飞——关注行为而非类型print(thing.fly())#示例duckDuck()personPerson()dogDog()print(叫)make_it_quack(duck)# 输出嘎嘎嘎make_it_quack(person)# 输出我学鸭子叫嘎嘎嘎make_it_quack(dog)# 输出汪汪假装是鸭子print(飞)make_it_fly(duck)make_it_fly(person)# make_it_fly(dog) # 会报错Dog 没有 fly 方法——但这不是类型问题