一、Python中的继承¶1 什么是继承¶我们接下来来聊聊Python代码中的“继承”类是用来描述现实世界中同一组事务的共有特性的抽象模型但是类也有上下级和范围之分比如生物 动物 哺乳动物 灵长型动物 人类 黄种人从哲学上说就是共性与个性之间的关系比如白马和马所以我们在OOP代码中也一样要体现出类与类之间的共性与个性关系这里就需要通过类的继承来体现。简单来说如果一个类A使用了另一个类B的成员属性和方法我们就可以说A类继承了B类同时这也体现了OOP中代码重用的特性2 继承的基本语法¶假设A类要继承B类中的所有属性和方法私有属性和私有方法除外class B(object): pass class A(B): pass a A() a.B中的所有公共属性 a.B中的所有公共方法案例Person类与Teacher、Student类之间的继承关系class Person(object): def eat(self): print(i can eat food!) def speak(self): print(i can speak!) class Teacher(Person): pass class Student(Person): pass teacher Teacher() teacher.eat() teacher.speak() student Student() student.eat() studnet.speak()3 与继承相关的几个概念¶继承一个类从另一个已有的类获得其成员的相关特性就叫作继承派生从一个已有的类产生一个新的类称为派生很显然继承和派生其实就是从不同的方向来描述的相同的概念而已本质上是一样的父类也叫作基类就是指已有被继承的类子类也叫作派生类或扩展类扩展在子类中增加一些自己特有的特性就叫作扩展没有扩展继承也就没有意义了单继承一个类只能继承自一个其他的类不能继承多个类单继承也是大多数面向对象语言的特性多继承一个类同时继承了多个父类 C、Python等语言都支持多继承4 单继承¶单继承一个类只能继承自一个其他的类不能同时继承多个类。这个类会有具有父类的属性和方法。基本语法# 1、定义一个共性类父类 class Person(object): pass # 2、定义一个个性类子类 class Student(Person): pass案例比如汽车可以分为两种类型汽油车、电动车# 1、定义一个共性类车类 class Car(object): def run(self): print(i can run) # 2、定义汽油车 class GasolineCar(Car): pass # 3、定义电动车 class EletricCar(Car): pass bwm GasolineCar() bwm.run()5 单继承(多层继承)¶在Python继承中如A类继承了B类B类又继承了C类。则根据继承的传递性则A类也会自动继承C类中所有属性和方法公共class C(object): def func(self): print(我是C类中的相关方法func) class B(C): pass class A(B): pass a A() a.func()6 编写面向对象代码中的常见问题¶6.1 类名问题¶问题在定义类时其没有遵循类的命名规则答在Python中类理论上是区分大小写的在Python中类可以全部大写也可以全部小写。但是要遵循一定的命名规范首字母必须是字母或下划线其中可以包含字母、数字和下划线而且要求其命名方式采用大驼峰。电动汽车EletricCar父类Father子类Son6.2 继承问题¶问题2父类一定要继承object么Car(object)答在Python面向对象代码中建议在编写父类时让其自动继承object类。但是其实不写也可以因为默认情况下Python中的所有类都继承自object。6.3 书写问题¶问题初始化对象属性的魔法方法?答:在定义魔术方法__init__名字经常会错误的写为__int__7 多继承¶什么是多继承Python语言是少数支持多继承的一门编程语言所谓的多继承就是允许一个类同时继承自多个类的特性。基本语法class B(object): pass class C(object): pass class A(B, C): pass a A() a.B中的所有属性和方法 a.C中的所有属性和方法案例汽油车、电动车 混合动力汽车汽车 电动class GasolineCar(object): def run_with_gasoline(self): print(i can run with gasoline) class ElectricCar(object): def run_with_eletric(self): print(i can run with eletric) class HybridCar(GasolineCar, ElectricCar): pass tesla HybridCar() tesla.run_with_gasoline() tesla.run_with_eletric()注意虽然多继承允许我们同时继承自多个类但是实际开发中应尽量避免使用多继承因为如果两个类中出现了相同的属性和方法就会产生命名冲突。8 子类重写父类属性和方法什么是重写重写也叫作覆盖就是当子类成员与父类成员名字相同的时候从父类继承下来的成员会重新定义此时通过子类实例化出来的对象访问相关成员的时候真正其作用的是子类中定义的成员注意: 如果子类中的属性和方法与父类中的属性或方法同名则子类中的属性或方法会对父类中同名的属性或方法进行覆盖重写class Father(object): # 属性 # 方法 class Son(Father): # 默认继承父类属性和方法 # 自己的属性和方法重写的意义?继承让子类继承父类的所有公共属性和方法但是如果仅仅是为了继承公共属性和方法继承就没有实际的意义了应该是在继承以后子类应该有一些自己的属性和方法。Animal 的子类 Cat和Dog 继承了父类的属性和方法但是我们狗类Dog 有自己的叫声汪汪叫猫类 Cat 有自己的叫声 喵喵叫 这时我们需要对父类的 call() 方法进行重构。如下class Animal(object): def eat(self): print(i can eat) def call(self): print(i can call) class Dog(Animal): pass class Cat(Animal): pass wangcai Dog() wangcai.eat() wangcai.call() miaomiao Cat() miaomiao.eat() miaomiao.call()Dog、Cat子类重写父类Animal中的call方法class Animal(object): def eat(self): print(吃) # 公共方法 def call(self): print(叫) class Dog(Animal): # 重写父类的call方法 def call(self): print(汪汪叫) class Cat(Animal): # 重写父类的call方法 def call(self): print(喵喵叫) wangcai Dog() wangcai.eat() wangcai.call() miaomiao Cat() miaomiao.eat() miaomiao.call()思考重写父类中的call方法以后此时父类中的call方法还在不在答还在只不过是在其子类中找不到了。类方法的调用顺序当我们在子类中重构父类的方法后Cat子类的实例先会在自己的类 Cat 中查找该方法当找不到该方法时才会去父类 Animal 中查找对应的方法。9 super()调用父类属性和方法¶super()作用调用父类属性或方法super()完整写法super(当前类名, self).属性或方法()在Python3以后版本中调用父类的属性和方法我们只需要使用super().属性或super().方法名()就可以完成调用了。案例Car汽车类、GasolineCar汽油车、ElectricCar电动车class Car(object): def __init__(self, brand, model, color): self.brand brand self.model model self.color color def run(self): print(i can run) class GasolineCar(Car): def run(self): print(i can run with gasoline) class ElectricCar(Car): def __init__(self, brand, model, color, battery): super().__init__(brand, model, color) # 电池属性 self.battery battery def run(self): print(fi can run with electrici have a {self.battery} kwh battery) bwm GasolineCar(宝马, X5, 白色) bwm.run() tesla ElectricCar(特斯拉, Model S, 红色, 70) tesla.run()class Car(object): def __init__(self, brand, model, color): self.brand brand self.model model self.color color def run(self): print(i can run) class GasolineCar(Car): def run(self): print(i can run with gasoline) class ElectricCar(Car): def __init__(self, brand, model, color, battery): super().__init__(brand, model, color) # 电池属性 self.battery battery def run(self): print(fi can run with electrici has a {self.battery} kwh battery) print(ElectricCar.__mro__) print(ElectricCar.mro())说明有MRO方法解析顺序可知在类的继承中当某个类创建了一个对象时调用属性或方法首先在自身类中去寻找如找到则直接使用停止后续的查找。如果未找到继续向上一级继承的类中去寻找如找到则直接使用没有找到则继续向上寻找...直到object类这就是Python类继承中其方法解析顺序。综上object类还是所有类的基类因为这个查找关系到object才终止二、Python中多态(了解)¶1 什么是多态¶多态指的是一类事物有多种形态。定义多态是一种使用对象的方式子类重写父类方法调用不同子类对象的相同父类方法可以产生不同的执行结果。不同对象 使用相同方法 产生不同的执行结果。① 多态依赖继承不是必须的② 子类方法必须要重写父类方法首先定义一个父类其可能拥有多个子类对象。当我们调用一个公共方法接口时传递的对象不同则返回的结果不同。多态好处调用灵活有了多态更容易编写出通用的代码做出通用的编程以适应需求的不断变化2 多态原理图¶公共接口service就是多态的体现随着传入水果对象的不同能返回不同的结果。3 多态代码实现¶多态可以基于继承也可以不基于继承 首先定义一个父类其可能拥有多个子类对象。当我们调用一个公共方法接口时传递的对象不同则返回的结果不同。 class Fruit(object): def makejuice(self): print(i can make juice) class Apple(Fruit): # 重写父类方法 def makejuice(self): print(i can make apple juice) class Banana(Fruit): # 重写父类方法 def makejuice(self): print(i can make banana juice) class Orange(Fruit): # 重写父类方法 def makejuice(self): print(i can make orange juice) # 定义一个公共接口专门用于实现榨汁操作 def service(obj): # obj要求是一个实例化对象可以传入苹果对象/香蕉对象 obj.makejuice() # 调用公共方法 service(Orange())扩展在Python中还有哪些多态的案例呢¶ 多态体现加号只有一个但是不同的对象调用方法其返回结果不同。如果加号的两边都是数值类型的数据则加号代表运算符如果加号的两边传入的是字符串类型的数据则加号代表合并操作返回合并后的字符串a b ab如果加号的两边出入序列类型的数据则加号代表合并操作返回合并后的序列[1, 2, 3] [4, 5, 6] [1, 2, 3, 4, 5, 6]三、面向对象其他特性重点¶1 类属性¶Python中属性可以分为实例属性和类属性。类属性就是 类对象中定义的属性它被该类的所有实例对象所共有。通常用来记录 与这类相关 的特征类属性 不会用于记录 具体对象的特征。在Python中一切皆对象。类也是一个特殊的对象我们可以单独为类定义属性。class Person(object): # 定义类属性 count 0 def __init__(self, name, age): self.name name self.age age p1 Person(Tom, 23) p2 Person(Harry, 26)2 类属性代码实现¶定义count类属性用于记录实例化Person类产生对象的数量。class Person(object): # 定义类属性count count 0 # 定义一个__init__魔术方法用于进行初始化操作 def __init__(self, name): self.name name # 对count类属性进行1操作用于记录这个Person类一共生成了多少个对象 Person.count 1 # 1、实例化对象p1 p1 Person(Tom) p2 Person(Harry) p3 Person(Jennifer) # 2、在类外部输出类属性 print(f我们共使用Person类生成了{Person.count}个实例对象)3 类方法¶为什么需要类方法在面向对象中特别强调数据封装性。所以不建议直接在类的外部对类属性进行直接获取。所以我们如果想操作类属性建议使用类方法。class Tool(object): # 定义一个类属性count count 0 # 定义一个__init__初始化方法 def __init__(self, name): self.name name Tool.count 1 # 封装一个类方法专门实现对Tool.count类属性进行操作 classmethod def get_count(cls): print(f我们使用Tool类共实例化了{cls.count}个工具) t1 Tool(斧头) t2 Tool(榔头) t3 Tool(铁锹) Tool.get_count()类方法主要用于操作类属性或类中的其他方法。4 静态方法¶在开发时如果需要在类中封装一个方法这个方法① 既 不需要访问实例属性或者调用实例方法② 也 不需要访问类属性或者调用类方法这个时候可以把这个方法封装成一个静态方法# 开发一款游戏 class Game(object): # 开始游戏打印游戏功能菜单 staticmethod def menu(): print(1、开始游戏) print(2、游戏暂停) print(3、退出游戏) # 开始游戏、打印菜单 Game.menu()5 综合案例¶需求分析:​ 设计一个Game类属性​ 定义一个类属性top_score记录游戏的历史最高分​ 定义一个实例属性player_name记录当前游戏的玩家姓名方法​ 静态方法show_help显示游戏帮助信息​ 类方法show_top_score显示历史最高分​ 实例方法start_game开始当前玩家的游戏实例代码: 设计一个Game类 属性 定义一个类属性top_score记录游戏的历史最高分 定义一个实例属性player_name记录当前游戏的玩家姓名 方法 静态方法show_help显示游戏帮助信息 类方法show_top_score显示历史最高分 实例方法start_game开始当前玩家的游戏 class Game(object): # 1、定义类属性 top_score 0 # 2、定义__init__初始化方法实现 def __init__(self, player_name): self.player_name player_name # 3、定义静态方法 staticmethod def show_help(): print(- * 40) print(【Start】开始游戏) print(【Stop】结束游戏) print(- * 40) # 4、定义类方法 专门用于调用类属性封装性 classmethod def show_top_score(cls): print(f本游戏的历史最高分{Game.top_score}) # 5、定义实例方法 def start_game(self): print(f{self.player_name}开始游戏) Game.show_help() # 调用静态方法 Game.show_top_score() # 调用类方法 game Game(itheima) game.start_game()