Python面向对象三大特征 封装、继承、多态
作者:J.FengS
1、封装
封装: 根据 职责 将 属性 和 方法 封装到一个 抽象的类 中将数据(属性)和行为(方法)包装到类对象中。在方法内部对属性进行操作,在类对象的外部调用方法。这样无需关心方法
- 内部的具体实现,从而隔离了复杂度
- 封装是面向对象编程的一大特点
- 面向对象编程的第一步。将属性和方法封装到一个抽象的类中
- 外界使用类创建对象,然后让对象调用方法
- 对象方法的细节都封装在类的内部
前言:根据需求分析,完成案例
1 class Person: 2 def __init__(self,name,weight): 3 self.name = name 4 self.weight = weight 5 6 # 内置函数,可设置print打印输出地址时的特定显示,因此必须要有返回值 7 def __str__(self): 8 return "我是%s,体重是%.1f公斤"%(self.name,self.weight) 9 10 def run(self): 11 self.weight -= 0.5 12 print("%s通过跑步体重减少"%self.name) 13 def eat(self): 14 self.weight+=1 15 print("%s吃太多,体重增加"%self.name) 16 17 p1 = Person("小明",65) 18 print(p1) #我是小明,体重是65.0公斤 19 p1.eat() #小明吃太多,体重增加 20 p1.run() #小明通过跑步体重减少 21 print(p1) #我是小明,体重是65.5公斤 22 print("*"*25) 23 p2 = Person("小美",45) 24 print(p2) #我是小美,体重是45.0公斤 25 p2.eat() #小美吃太多,体重增加 26 p2.run() #小美通过跑步体重减少 27 print(p2) #我是小美,体重是45.5公斤 28 print(p1) #我是小明,体重是65.5公斤
注意:在开发时,被使用的类需要先开发
1 #创建房子类 2 class House: 3 def __init__(self,house_type,area): 4 self.house_type = house_type 5 self.area = area 6 self.free_area = area 7 self.item_list = [] 8 9 #添加家具 10 def add_item(self,item): 11 #判断面积是否足够添加家具 12 if item.area<self.free_area: 13 self.item_list.append(item.name) 14 self.free_area -= item.area 15 print("添加%s,占用面积%.1f"%(item.name,item.area)) 16 else: 17 print("面积不足无法添加") 18 19 #输出房子打印 20 def __str__(self): 21 #Python可以自动将一对括号内的代码连接到一起 22 return ("该房子的户型:%s\n总面积为:%.1f平米\n剩余面积为:%.1f平米\n家具:%s" 23 %(self.house_type,self.area,self.free_area,self.item_list)) 24 25 #创建家具对象 26 jj1 = HouseItem("席梦思",4) 27 jj2 = HouseItem("衣柜",2) 28 jj3 = HouseItem("餐桌",1.5) 29 print(jj1) #家具席梦思,占地面积为:4.0平米 30 print(jj2) #家具衣柜,占地面积为:2.0平米 31 print(jj3) #家具餐桌,占地面积为:1.5平米 32 print("-"*30) 33 #创建房子对象,并添加家具 34 hs = House("大平层",6) 35 hs.add_item(jj1) 36 hs.add_item(jj2) 37 hs.add_item(jj3) 38 print("-"*30) 39 print(hs) 40 41 #运行结果 42 家具席梦思,占地面积为:4.0平米 43 家具衣柜,占地面积为:2.0平米 44 家具餐桌,占地面积为:1.5平米 45 ------------------------------ 46 添加席梦思,占用面积4.0 47 面积不足无法添加 48 添加餐桌,占用面积1.5 49 ------------------------------ 50 该房子的户型:大平层 51 总面积为:6.0平米 52 剩余面积为:0.5平米 53 家具:['席梦思', '餐桌']
1.1 私有属性和私有方法
提高程序的安全性:
在实际开发中,对象的某些属性或方法只希望在类的内部被使用,而不希望在外部被访问到
在Python
中没有专门的修饰符用于属性的私有,如果该属性不希望在类对象外部被访问,前面使用两个“ _ ”(私有属性)
在Python中其实并没有真正的私有属性、私有方法:
- 给属性、方法命名时,实际是对名称做了一些特殊处理,使得类对象无法直接访问。
- 但是如果一定要从外界访问私有属性、私有方法的话,那么只需要在私有属性、私有方法前加上_类名
例:stu_1._Student__age stu_1._Student__show_1()
注意:切记在开发时,不要以这种方式去访问私有属性和私有方法
1 #私有属性、私有方法 2 class Student: 3 def __init__(self,name,age): 4 self.name = name 5 self.__age = age #使用(__属性名)的方式将age声明为私有属性 6 7 def show(self): 8 print(self.name,"的年龄为:",self.__age) #可以在类的内部使用私有属性 9 10 def __show_1(self): #使用(__方法名)的方式将show_1定义为私有方法 11 print("这是{}的私有方法".format(self.name)) 12 13 stu_1 = Student("张三",20) 14 stu_1.show() #由此可见可以在类的内部使用私用属性 15 print(stu_1.name) 16 #print(stu_1.__age) #代码报错,因为在类对象中并不能访问类的私有属性 17 18 #stu_1.__show_1() #代码报错,因为在类对象中并不能访问类的私有属性 19 20 """ 21 在Python中起始并没有真正的私有属性、方法: 22 给属性、方法命名时,实际是对名称做了一些特殊处理,使得类对象无法直接访问。 23 但是如果一定要从外界访问私有属性、方法的话,那么只需要在私有属性、方法前加上_类名 24 例:stu_1._Student__age stu_1._Student__show_1() 25 """ 26 print(stu_1._Student__age) 27 stu_1._Student__show_1() 28 29
运行结果:
张三 的年龄为: 20
张三
20
这是张三的私有方法
注意:
子类能继承父类的私有属性和私有方法
1 class A: 2 def __init__(self): 3 self.num1 = 100 4 self.__num2 = 200 5 6 def __fun(self): 7 print("这是一个私有方法",self.__num2) 8 9 class B(A): 10 pass 11 12 b = B() 13 print(b._A__num2) 14 # print(b._b__num2) #代码报错,因为子类不能继承父类的私有方法和私有属性 15 b._A__fun() 16 # b._B__fun() 17 print(dir(b)) 18 """ 19 运行结果: 20 200 21 这是一个私有方法 200 22 ['_A__fun', '_A__num2', '__class__', 23 '__delattr__', '__dict__', '__dir__', 24 '__doc__', '__eq__', '__format__', '__ge__', 25 '__getattribute__', '__gt__', '__hash__', 26 '__init__', '__init_subclass__', 27 '__le__', '__lt__', '__module__', 28 '__ne__', '__new__', '__reduce__', 29 '__reduce_ex__', '__repr__', '__setattr__', 30 '__sizeof__', '__str__', '__subclasshook__', 31 '__weakref__', 'num1'] 32 """
子类对象可以调用父类的公有方法,在父类的公有方法中调用父类的私有方法和私有属性,那么子类对象就可以间接的使用该共有方法访问父类的私有属性和私有方法
2、继承
- 实现代码的重用,相同的代码不需要重复编写
- 继承的语法格式
class 类名(父类名): pass
- 子类 继承 父类,可以直接 享受 父类中已经封装好的方法,不需要再次开发
- 子类 中应该根据 职责,封装 子类特有的 属性和方法
- 继承的传递性:子类拥有父类以及父类和父类中封装的所有属性和方法
1 #单继承 2 class Animal: 3 def eat(self): 4 print("吃") 5 def drink(self): 6 print("喝") 7 def run(self): 8 print("跑") 9 def sleep(self): 10 print("睡") 11 12 class Dog(Animal): 13 def bark(self): 14 print("叫") 15 16 class Cat(Animal): 17 def catch(self): 18 print("抓老鼠") 19 20 class XiaoTianQuan(Dog): 21 def fly(self): 22 print("飞") 23 24 xtq = XiaoTianQuan() 25 xtq.eat() 26 xtq.bark() 27 #xtq.catch() #报错,因为xiaotianquan的父类及父类的父类都没有该方法
2.1 方法的重写
如果在子类中重写了父类的方法,在子类对象调用方法时,会调用子类重写的方法
1 #方法重写 2 class Animal: 3 def eat(self): 4 print("吃") 5 def drink(self): 6 print("喝") 7 def run(self): 8 print("跑") 9 def sleep(self): 10 print("睡") 11 12 class Dog(Animal): 13 def bark(self): 14 print("叫") 15 16 class Cat(Animal): 17 def catch(self): 18 print("抓老鼠") 19 20 class XiaoTianQuan(Dog): 21 def fly(self): 22 print("飞") 23 24 def bark(self): 25 print("像神一样的叫") 26 27 xtq = XiaoTianQuan() 28 xtq.bark() #像神一样的叫
2.2 在子类方法中调用父类方法
- 方法一:使用
super().
方法名 推荐使用 - 方法二:使用父类名.方法名
(self)
在python2.x
中 只能使用这个方式
1 #在子类方法中调用父类方法 2 class Animal: 3 def eat(self): 4 print("动物吃") 5 def drink(self): 6 print("动物喝") 7 def run(self): 8 print("动物跑") 9 def sleep(self): 10 print("动物睡") 11 12 class Dog(Animal): 13 def bark(self): 14 print("狗叫") 15 16 class Cat(Animal): 17 def catch(self): 18 print("抓老鼠") 19 20 class XiaoTianQuan(Dog): 21 def fly(self): 22 print("像神一样的飞") 23 def bark(self): 24 #1.针对子类特有的需求,编写的代码 25 print("像神一样的叫") 26 #2.方法一:使用super().调用原本封装在父类中的方法 27 super().bark() 28 #3.方法二:使用super().调用原本封装在父类中的方法 29 Dog.bark(self) 30 #4.增加子类其他的代码 31 print("------------------") 32 xtq = XiaoTianQuan() 33 xtq.bark() 34 """
运行结果:
像神一样的叫
狗叫
狗叫
------------------
"""
2.3 多继承
概念:
子类 可以拥有 多个父类,并且具有 所有父类的属性 和 方法
例如:孩子 会继承自己 父亲 和 母亲 的特性
1 #多继承 2 class A: 3 def text(self): 4 print("这是A类的text方法") 5 class B: 6 def fun(self): 7 print("这是B类的fun方法") 8 class C(A,B): 9 pass 10 c = C() 11 c.text() #这是A类的text方法 12 c.fun() #这是B类的fun方法
注意事项:
如果 不同的父类 拥有相同的属性名或者相同的方法名,子类对象在调用方法和属性时,会调用那个父类的呢?
提示:在开发时,应该尽量避免这种容易产生混淆的情况! ——如果父类之间存在同名属性或者同名方法,应该尽量避免使用
多继承图:
Python
中的mro
--方法搜索顺序(了解)Python
中针对 类 提供了一个内置属性__mro__
可以查看 方法的搜索顺序MRO
是method tesolution order
,主要用于 在多继承判断 方法 、属性 的调用路径- 子类对象在执行父类的同名方法时,是按照
MRO
输出的从做到右的顺序去执行的
1 #多继承属性、方法同名 2 class A: 3 num = 100 4 def text(self): 5 print("这是A类的text方法") 6 class B: 7 num = 200 8 def text(self): 9 print("这是B类的fun方法") 10 class C(B,A): 11 pass 12 c_1 = C() 13 c_1.text() 14 print(c_1.num) 15 #确定C类对象的调用方法的路径 16 print(C.__mro__) 17 """ 18 运行结果: 19 这是B类的fun方法 20 200 21 (<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>) 22 """
2.4 新式类和经典类
object
是 Python
为所有对象提供的基类,提供有内置的属性和方法,可以使用dir
来查看
1 class A(object): 2 pass 3 print(dir(A)) 4 5 """ 6 运行结果所显示的属性和方法都是由object提供的,因为我并没有为A类设置任何的属性和方法 7 ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', 8 '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', 9 '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', 10 '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', 11 '__str__', '__subclasshook__', '__weakref__'] 12 """
- 新式类:以
object
为基类,推荐使用 - 经典类:不以
object
为基类,不推荐使用 - 在
python3.x
中定义类,如果没有指定父类,会默认使用object
类作为该类的基类,因此,python3.x
中所有的类都是新式类 - 在
python2.x
中定义类,如果没有指定父类,不会使用object
作为基类。 - 经典类和新式类在多继承时——会影响到方法的搜索顺序
为了保证编写的代码在Python2.x
和Python3.x都
能够运行,在定义类的时,如果没有父类,建议统一继承object
class 类名(object): pass
3、多态
- 不同的对象,调用相同的方法,产生不同的执行结果,增加代码的灵活度
- 多态可以增加代码的灵活度
- 以继承和重写父类方法为前提
- 是调用方法的技巧,不会影响到类的内部设计
1 #多态的案例演示 2 """ 3 1.定义Dog类,并中封装方法game 4 普通的狗子蹦蹦跳跳 5 2.定义GodDog类,继承自狗类,并重写game方法 6 神狗在天上玩耍 7 3.定义ErHa类,继承自狗类,并重写game方法 8 傻狗在地上滚 9 4.定义Person类,并封装一个和狗玩的方法game_with_dog方法 10 在方法内部,直接让 狗对象 调用game方法 11 """ 12 class Dog(object): 13 def __init__(self,name): 14 self.name = name 15 16 def game(self): 17 print("蹦蹦跳跳") 18 19 class GodDog(Dog): 20 def game(self): 21 print("%s在天上飞"%self.name) 22 23 class ErHa(Dog): 24 def game(self): 25 print("%s这个傻狗在地上滚"%self.name) 26 27 class Person(object): 28 def __init__(self,name): 29 self.name = name 30 def ame_with_dog(self,dog): 31 print("%s在和%s玩耍"%(self.name,dog.name)) 32 dog.game() 33 34 #创建二哈 35 eh = ErHa("哈士奇") 36 #创建哮天犬 37 xtq = GodDog("哮天犬") 38 #创建二郎神 39 els = Person("二郎神") 40 els.ame_with_dog(eh) 41 print("*"*30) 42 els.ame_with_dog(xtq) 43 """ 44 运行结果 45 二郎神在和哈士奇玩耍 46 哈士奇这个傻狗在地上滚 47 ****************************** 48 二郎神在和哮天犬玩耍 49 哮天犬在天上飞 50 """
由上面代码可以看出,二郎神在调用和狗玩的方法时,当传入的狗对象不同时,代码的运行结果也不相同
到此这篇关于Python面向对象三大特征 封装、继承、多态的文章就介绍到这了,更多相关Python面向对象 封装、继承、多态内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!