面向对象特性继承概念子类默认继承父类的所以属性和方法与此同时子类也可以重写父类属性和方法现实例子生活中继承一般指的是子女继承父辈的财产继承的基本语法基本语法#父类B Class B(object): pass #子类A Class A(B): pass注意事项在python中所有类默认继承object类object是顶级类或者基类其他子类叫做派生类。解释自动拥有B中的所有公共属性和方法私有属性和方法不支持继承实例与继承相关的几个概念继承一个类从另一个已有的类获得其成员的相关特性就叫作继承派生从一个已有的类产生一个新的类称为派生 很显然继承和派生其实就是从不同的方向来描述的相同的概念而已本质上是一样的父类也叫作基类就是指已有被继承的类子类也叫作派生类或扩展类扩展在子类中增加一些自己特有的特性就叫作扩展没有扩展继承也就没有意义了继承后必须要有扩展单继承一个类只能继承自一个其他的类不能继承多个类单继承也是大多数面向对象语言的特性多继承一个类同时继承了多个父类C、Python 等语言都支持多继承python中的单继承单继承一个类只能继承自一个其他的类不能继承多个类单继承也是大多数面向对象语言的特性基本语法#父类B Class B(object): pass #子类A Class A(B): pass案例比如汽车可以分为两种汽油车电动车#1.定义一个共性类车类 class Car(object): def run(self): print(汽车开动) #2.定义汽油车 class Q_Car(Car): pass #3.定义电动车 class D_Car(Car): pass qcar Q_Car() qcar.run()单继承特性多层继承传递性在python继承中如A类继承了B类B类又继承了C类。则根据继承的传递性则A类也会自动继承C类中所有属性和方法公共class A(object): def func(self): print(我是A类中的相关方法func) class B(A): pass class C(A): pass a A() a.func()输出结果多继承概念Python 语言是少数支持多继承的一门编程语言所谓的多继承就是允许一个类同时继承自多个类的特性。基本语法class B(object): pass class C(object): pass class A(B,C): pass a A() a.B中的所有属性和方法 a.A中的所有属性和方法案例汽油车、电动车 混合动力汽车汽车电动#定义一个类为汽油车 class QCar(object): def run_qcar(self): print(QCar) #定义一个类为电动车 class DCar(object): def run_Dcar(self): print(DCar) #定义一个汽车 class Car(QCar, DCar): pass test Car() test.run_qcar() test.run_Dcar()输出结果注意虽然多继承允许我们同时继承自多个类但是实际开发中应尽量避免使用多继承因为如果两个类中出现了相同的属性和方法就会产生命名冲突。子类扩展重写父类属性和方法扩展特性继承让子类继承父类的所有公共属性和方法但是如果仅仅是为了继承公共属性和方法继承就没有实际的意义了应该是在继承以后子类应该有一些自己的属性和方法。什么是重写 重写也叫作覆盖就是当子类成员与父类成员名字相同的时候从父类继承下来的成员会重新定义此时通过子类实例化出来的对象访问相关成员的时候真正起作用的是子类中定义的成员class Fatherobject属性方法class SonFather)父类属性和方法自己的属性和方法如果子类中的属性和方法与父类中的属性或者方法同名则子类中的属性或者方法回对父类中同名的属性或者方法进行覆盖重写上面单继承例子中 Animal 的子类 Cat 和 Dog 继承了父类的属性和方法但是我们狗类 Dog 有自己的叫声 汪汪叫 猫类 Cat 有自己的叫声 喵喵叫 这时我们需要对父类的 call () 方法进行重构。如下#定义一个动物父类 class Animal: def eat(self): print(我想eat) def call(self): print(我想call) class Dog(Animal): def call(self): print(dog想call) class Cat(Animal): #继承Animal类并重写父类中的call方法 def call(self): print(cat想call) #定义一个狗对象 dog Dog() dog.eat() dog.call() #定义一个猫对象 cat Cat() cat.eat() cat.call()输出结果思考重写父类中的 call 方法以后此时父类中的 call 方法还在不在答还在只不过是在其子类中找不到了。类方法的调用顺序当我们在子类中重构父类的方法后Cat 子类的实例先会在自己的类 Cat 中查找该方法当找不到该方法时才会去父类 Animal 中查找对应的方法。super()调用父类属性和方法super()调用父类属性或方法完整写法super(当前类名, self).属性或方法()在 Python3 以后版本中调用父类的属性和方法我们只需要使用super().属性或super().方法名()就可以完成调用了。案例Car 汽车类、QCar 汽油车、DCar 电动车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 DCar(Car): def run(self): print(i can run with DCar) class QCar(Car): def __init__(self, brand, model, color,battery): #强制调用父类中的__init__初始话方法用于实现对brand/color/model属性进行初始化 super().__init__(brand,model,color) self.battery battery def run(self): print(i can run with QCar) # 实例化对象 qcar QCar(宝马,s,粉色,70) print(qcar.brand) print(qcar.model) print(qcar.color) qcar.run()输出结果MRO属性或者MRO方法方法解析顺序MOO(Method Resolution Order):方法解析顺序我们可以通过类名.__mro__或者类名.mro获得“类的层次结构”方法解析顺序也是按照这个“类的层次结构”寻找到。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 DCar(Car): def run(self): print(i can run with DCar) class QCar(Car): def __init__(self, brand, model, color,battery): #强制调用父类中的__init__初始话方法用于实现对brand/color/model属性进行初始化 super().__init__(brand,model,color) self.battery battery def run(self): print(i can run with QCar) print(QCar.__mro__) print(QCar.mro())输出结果类属性的静态方法注解staticmethod在开发时如果需要在类中封装一个方法这个方法① 既不需要访问实例属性或者调用实例方法② 也不需要访问类属性或者调用类方法这个时候可以把这个方法封装成一个静态方法#开发一款游戏 class Game(object): #开始游戏打印游戏功能菜单 这个功能既不需要调用对象属性也不要调用对象方法 #也不需要调用类属性也不需要调用类方法 staticmethod def menu(): print(1.开始游戏) print(2.游戏暂停) print(3.退出游戏) #开始游戏打印菜单 #调用方式 Game.menu() #实例化对象 game Game() game.menu()输出结果面向对象特性多态概念多态是同一类事务具有的多种形态。不同的对象调用同一个接口方法表现出不同的状态成为多态定义多态是一种使用对象的方式子类重写父类方法调用不同子类对象的相同父类方法可以产生不同的执行结果。不同对象 使用相同方法 产生不同的执行结果。① 多态依赖继承不是必须的② 子类方法必须要重写父类方法首先定义一个父类其可能拥有多个子类对象。当我们调用一个公共方法接口时传递的对象不同则返回的结果不同。好处调用灵活有了多态更容易编写出通用的代码做出通用的编程以适应需求的不断变化多态的原理图公共接口service就是多态的体现随着传入水果对象的不同能返回不同的结果多态代码的实现多态可以基于继承也可以不基于继承 多态特性一种公共方法随着传入参数对象不同则同一个方法可以返回不同的结果 class Fruit(object): def makejuice(self): print(i can make juice) #定义Apple类继承Fruit类并重写父类中的makejuice方法 class Apple(Fruit): def makejuice(self): print(i can make apple juice) #定义Banana类继承Fruit类并重写父类中的makejuice方法 class Banana(Fruit): def makejuice(self): print(i can make banana juice) #定义一个公共的方法用于调用makejuice功能 def service(obj): obj.makejuice() #传入一个苹果对象 service(Apple()) #传入一个香蕉对象 service(Banana())输出结果编写面向对象代码中常见问题问题1在定义类时其没有遵循类的命名规则答在 Python 中类理论上是不区分大小写的。但是要遵循一定的命名规范首字母必须是字母或下划线其中可以包含字母、数字和下划线而且要求其命名方式采用大驼峰。电动汽车ElectricCar父类Father子类Son问题2父类一定要继承object答无论是否书写继承object自定义类都会默认隐式继承 object全部都是新式类两种写法效果完全一致。问题3打印属性和方法时不是只能使用print输出也可以使用return。在实际打印对象信息还建议使用__str__return:class Person: def get_info(self): return fName: {self.name}, Age: {self.age} p Person(Bob, 30) info p.get_info() # 获取返回值__str__:class Person: def __str__(self): return fPerson(name{self.name}, age{self.age}) p Person(Charlie, 35) print(p) # 自动调用__str__函数的闭包变量在内存中的存储形式前言全局变量和局部变量全局变量的访问范围1.在全局作用域中可以正常访问全局变量2.在局部作用域中也可以正常访问全局变量局部变量的访问范围1.在局部作用域中可以正常访问局部变量2.在全局作用域中无法正常访问局部变量因为函数执行完毕后其内部的变量会随之消失扩展为什么函数执行完毕其内部的变量会随着消失答计算机的垃圾回收机制#函数外部的区域 全局作用域 #定义一个变量 a 10 def func(): #函数内部的区域 局部作用域 b 20 #尝试访问全局变量a print(在局部内尝试访问全局变量a) print(a) print( 局部变量的访问范围 在局部作用域中可以访问局部变量但是在全局作用域中无法访问局部变量其内部的变量会自动被内存所回收) print(b) func() print(全局变量的访问范围 在全局作用域中可以访问全局变量) print(a) #全局变量的访问范围 在全局作用域中可以访问全局变量 #局部变量的访问范围 在局部作用域中可以访问局部变量但是在全局作用域中无法访问局部变量其内部的变量会自动被内存所回收输出结果数据的闭包--计算机的垃圾回收机制全局变量与局部变量在栈内存和数据区全局变量存储在数据区Data Segment包括初始化数据段.data和未初始化数据段.bss。它们的生命周期从程序启动到程序结束作用域为整个程序。局部变量存储在栈内存Stack Segment生命周期仅限于函数调用期间作用域仅限于函数内部。闭包与垃圾回收机制闭包的本质闭包是函数与其引用环境的组合。当内部函数引用外部函数的局部变量时即使外部函数执行完毕这些变量仍被保留。闭包通过延长变量的生命周期突破局部变量的作用域限制。垃圾回收关键点未被引用的对象会被标记为可回收。闭包引用的变量不会被释放即使外部函数已结束。全局变量始终不被回收直到程序终止。典型闭包示例def outer(): x 10 def inner(): print(x) # 引用外部变量形成闭包 return inner closure outer() closure() # 输出10证明x未被销毁内存管理对比栈内存特性自动管理高效但容量有限后进先出LIFO结构存储函数调用上下文和局部变量数据区特性静态分配生命周期固定存储全局/静态变量和常量内存占用持续到程序结束变量生命周期管理延长生命周期的方法使用闭包可主动控制变量生命周期。闭包函数保持对外部变量的引用阻止垃圾回收机制释放这些变量。内存泄漏风险不当使用闭包可能导致内存泄漏。当闭包长期持有大对象引用时这些对象无法被及时回收。应避免在闭包中缓存不必要的数据。问题我们也没有办法把函数内部的布局变量保留在函数嵌套的前提下内部函数使用了外部函数的变量并且外部函数返回了内部函数我们把这个使用外部函数的内部函数成为闭包闭包的构成条件三步走