本文章最初发布在 XJHui’s Blog,未经允许,任何人禁止转载!
注意:最新修改版本已发布在 这里,点击前往查看!
析构方法
当一个对象被删除或者被销毁时,python解释器默认会调用一个_del_()方法也叫析构方法。
方法特点
- _del_()方法是一个魔术方法
- 程序运行结束会释放所有内存,就是通过调用del方法实现的
- 在程序中删除一个对象也会调用del方法
使用方法
-
情况一:程序结束,解释器自动调用del方法释放内存:
class Animal: def __init__(self, name): self.name = name print('对象【{}】已经创建!'.format(self.name)) def __del__(self): print('正在回收内存,对象【{}】已被删除!'.format(self.name)) cat = Animal('小花猫')
运行结果:
-
情况二:程序中存在主动删除对象的内容:
class Animal: def __init__(self, name): self.name = name print('对象【{}】已经创建!'.format(self.name)) def __del__(self): print('正在回收内存,对象【{}】已被删除!'.format(self.name)) cat = Animal('小花猫') del cat inPut = input('程序等待中...') # 让程序一直运行避免与1相矛盾
运行结果:
OOP三大特征
-
封装
-
继承
-
多态
封装
-
定义:把内容封装到某个地方,便于以后使用
-
使用:通过初始化方法(init)将内容封装到对象中,然后通过对象直接获取或通过self获取
继承
单继承
-
定义:子类可以继承父类的内容【属性和方法】(父类有的子类都有,但子类有的父类不一定有)
-
案例:创建两个对象,其方法分别如下:
思路1:
class Cat: 喵喵叫 吃 喝 class Dog: 汪汪叫 吃 喝 cat = Cat() dog = Dog()
思路2:共有方法放在同一个类中
class Animal: 吃 喝 class Cat(Animal): # 继承动物类 喵喵叫 class Dog(Animal): # 继承动物类 汪汪叫 cat = Cat() dog = Dog()
注意:比较上面两种思路,2nd代码更简洁,这也是继承的优点
-
单继承子类语法:
class 子类名(父类名): # 子类继承父类 代码块
-
总结:
- 将多个类中共有的方法、属性提取到【父类】中,而特有的方法在各自类【子类】中。
- 继承可以极大地提高代码效率,避免代码过度冗余
多继承
继承单个父类称为单继承,继承多个父类就是多继承
-
多继承子类语法:
class 子类名(父类1名, 父类2名 ...): # 子类继承父类,多个父类之间使用逗号分隔 代码块
-
案例:创建孙悟空【类】其继承自神仙【类】和猴子【类】,并为其实例化一个对象
class Shenxian: def fly(self): print('神仙会飞!') class Monky: def chitao(self): print('猴子喜欢吃桃!') class Sunwukong(Shenxian, Monky): # 多继承中多个父类之间使用逗号分隔 def __init__(self, name): self.name = name print('创建【{}】对象成功!'.format(self.name)) swk = Sunwukong('孙悟空') swk.fly() # 调用Shenxian【类】的方法 swk.chitao() # 调用Monky【类】的方法
运行结果:
-
调试时输出异常:
本以为是python版本的原因才会与老师的输出结果不同,直到百度到下面这句话:
再看一眼我代码:
总结,感谢 rayshaw13:
- 当方法中已经使用了print,调用时不要再次使用,否则会多输出一个None
- 若必须使用print,那也可以在方法中使用return来避免多输出内容
同名方法
当多个父类中存在相同的方法的时候,应该调用哪一个?
-
案例:
class D: def eat(self): print('D.eat()') class C(D): # C【类】继承D【类】 def eat(self): print('C.eat()') class B(D): # B【类】继承D【类】 pass class A(B, C): # A【类】继承B【类】、C【类】 pass a = A() # 创建A类的实例对象 a.eat() # 调用a的eat方法,判断该eat方法属于谁
运行结果:
根据上例可知,其符合【广度优先遍历】的原则:
-
__mro__方法:
查看类的继承顺序(优先级)
print(A.mro()) # 注意是A.mro()【A类】 而不是实例对象a
运行结果:
总结:将此顺序与1st中的【最终顺序】比较可知是一致的。
继承的传递
子类继承父类,孙子类继承子类,孙子类可调用【父类】的方法
-
案例:判断下面程序能否正常输出:
class D: def eat(self): print('D.eat()') class C(D): pass class A(C): pass a = A() a.eat()
运行结果:
-
使用mro方法查看类的继承顺序:
print(A.mro())
运行结果:
-
总结:可以使用任意祖先【类】的方法。
重写方法
-
是什么?
在子类中有一个和父类相同名字的方法,子类中的方法会覆盖掉父类中的方法。
-
为什么?
父类的方法已经不能满足子类的需要,那么子类可以重写父类或者完善父类中的方法。
-
案例1:创建Keji【类】继承自父类Dog【类】,并重写父类Bark方法
class Dog: # 父类Dog【类】 def Bark(self): # 父类方法 print('汪汪汪...') class Keji(Dog): # 子类Keji【类】,继承父类Dog【类】 def Bark(self): # 重写父类方法 print('嗷嗷嗷...') kj = Keji() # 创建实例对象 kj.Bark() # 调用之类中法重写后的Bark方法
运行结果:
-
案例2:在父类方法基础上进行修改,以在init中添加实例属性为例
class Dog: def __init__(self, name, color): # 父类方法中原有2个参数 self.name = name self.color = color class Keji(Dog): def __init__(self, name, color, height): # 在父类方法的基础上填加1个新的参数 super().__init__(name, color) # 自动找到父类中的init方法,法1 # 法2:Dog.__init__(self, name, color) 注意self不能省略 self.height = height def __str__(self): return '【{}】 的颜色是:{} 身高是:{}'.format(self.name, self.color, self.height) kj = Keji('路由器', 'black', '10') print(kj)
运行结果:
-
案例3:在父类方法基础上进行修改,以普通方法为例
class Dog: # 父类Dog【类】 def __init__(self, name, color): self.name = name self.color = color def Bark(self): # 父类方法 print('汪汪汪...') class Keji(Dog): # 子类Keji【类】,继承父类Dog【类】 def __init__(self, name, color, height): super().__init__(name, color) # Dog.__init__(self, name, color) self.height = height def __str__(self): return '【{}】 的颜色是:{} 身高是:{}'.format(self.name, self.color, self.height) def Bark(self): # 重写父类方法 super().Bark() print('嗷嗷嗷...') kj = Keji('路由器', 'black', '10') # 创建实例对象 print(kj) kj.Bark()
运行结果:
类、实例属性
创建和使用
-
类属性:就是类对象所拥有的属性,它被所有类对象的实例对象所共有,类对象和实例对象均可以访问
class Test: name = '路由' # 类属性 test = Test() # 创建实例变量 print(test.name) # 通过实例变量访问类属性 print(Test.name) # 通过类变量访问类属性
运行结果:
-
实例属性:实例对象所拥有的属性,只能通过实例对象访问
class Test: name = '路由' # 类属性 def __init__(self, age): # 使用init方法定义实例属性 self.age = age test = Test(10) # 创建实例变量 print(test.age) # 通过实例变量访问实例属性 print(Test.age) # 通过类变量访问实例属性(错误)
运行结果:
属性的修改
-
类属性的修改:
-
错误方法:通过实例对象修改类属性
class Test: name = '路由' # 类属性 test = Test() # 创建实例对象 print(test.name) # 打印类属性 test.name = '湘湘' # 通过实例对象修改类属性 print(Test.name) # 再次打印类属性
运行结果:
注意:根据运行结果可知,通过实例对象修改类属性是行不通的,上面的做法只是新创建了一个实例属性。
-
正确方法:通过类对象修改类属性
class Test: name = '路由' # 类属性 test = Test() # 创建实例对象 print(test.name) # 打印类属性 Test.name = '湘湘' # 通过实例对象修改类属性的值 print(Test.name) # 再次打印类属性
运行结果:
-
总结:实例对象只拥有类属性的使用权,而修改权归类对象所有。
-
-
实例属性的修改:
实例属性只能通过实例对象访问,修改肯定也只能通过实例对象修改
class Test: name = '路由' # 类属性 def __init__(self, age): self.age = age # 实例属性 test = Test(2) # 创建实例对象 print(test.age) # 打印实例属性 test.age = 2.5 print(test.age)
运行结果:
类、静态方法
类方法
-
区别实例方法:
class People: name = '湘湘' # 实例方法 def printData(self): return self.name # 实例方法 @classmethod # 区别1:类方法前面需要添加@classmethod def printData(cls): # 区别2:类方法,默认参数为cls(可修改但不可省略) return cls.name
-
创建、使用:
class People: name = '湘湘' @classmethod def printData(cls): return cls.name # 返回类属性 print(People.printData()) # 通过类对象调用类方法 p1 = People() print(p1.printData()) # 通过实例对象调用类方法
运行结果:
-
使用类方法修改类属性:
class People: name = '湘湘' @classmethod def printData(cls): return cls.name # 返回类属性 @classmethod def changName(cls, data): cls.name = data # 通过类对象修改类属性 print(People.printData()) People.changName('路由') print(People.printData())
运行结果:
静态方法
-
区别实例方法:
class People: name = '湘湘' # 实例方法 def printData(self): return self.name # 静态方法 @staticmethod # 区别1:静态方法前面需要添加@staticmethod def getdata(): # 区别2:静态方法无默认参数 return People.name
-
创建、使用:
class People: name = '湘湘' @staticmethod def getdata(): return People.name print(People.getdata()) # 通过类对象访问静态变量 p1 = People() print(p1.getdata()) # 通过实例对象访问静态变量
运行结果:
注意:一般情况下是不会通过实例对象去调用静态方法的。
-
为什么使用静态方法?
- 主要用来存放逻辑性的代码,和类以及实例对象没有交互
- 因为可以直接通过类对象调用,从而避免了因创建实例对象而消耗的内存和空间
-
案例:返回系统时间
import time # 导入第三方的包 class Time: # 时间类 @staticmethod # 返回当前时间的静态方法 def getTime(): return time.strftime('%H:%M:%S', time.localtime()) print(Time.getTime()) # 通过类变量调用静态方法
运行结果:
实例、类、静态方法
- 实例方法:第一个参数是self(默认,可修改但不可省略),通过self可引用类属性或实例属性。若同时存在类属性和实例属性,实例属性优先级更高。
- 类方法:第一个参数是cls(默认,可修改但不可省略),通过cls可引用类对象的属性和方法。
- 静态方法:无默认参数,使用时必须通过类对象调用。
多态
基本概念
-
含义:
定义时的类型和使用时的类型不一样,此时就成为多态【同一种行为对于不同的子类对象有不同的行为表现】。
-
必须要遵守的条件:
- 继承:多态必须发生在子类和父类之间
- 重写:子类重写父类的方法
使用方法
-
案例:
class Animal: def showData(self): print('这是个动物类!') class Duck(Animal): # 有继承 def showData(self): # 有重写 print('这是个鸭子类!') duck = Duck() duck.showData()
运行结果:
总结:有继承和重写就是多态【个人看法】
-
优点:
- 增加程序的灵活性
- 增加程序的拓展性
鸭子类型
当看到一只鸟,走起路来像鸭子,游起泳来像鸭子,叫起来像鸭子,那么就可以把这只鸟承做鸭子。
-
案例(本案例与多态无关):
class Animal: # 动物类 def showData(self): print('这是个动物类!') class People: # 人类 def showData(self): print('这是个人类!') def Func(obj): # 调用传入对象的showData方法 obj.showData() listA = [Animal(), People()] # 创建两个实例对象 for obj in listA: # 枚举列表中的每个实例对象 Func(obj) # 将枚举的对象作为参数传入Func函数中
运行结果:
-
总结:上面案例中obj就是鸭子类型的参数,因为无论该参数是哪个对象的实例只要其包含showData方法,就能成功调用。
不足之处,欢迎留言,会及时回复,及时更正!
创作不易,感谢支持!