Python_基础_(面向对象三大特性)
Posted 一头牛这么多人放
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python_基础_(面向对象三大特性)相关的知识,希望对你有一定的参考价值。
## 在Python中的继承分为单继承和多继承
class ParentClass1: pass class ParentClass2: pass class SubClass(ParentClass1): # 单继承 pass class SubClass(ParentClass1,ParentClass2) # 多重继承 pass
# 在Python中可以继承多个类,在Java和C#中则只能继承一个类
# Python类如果继承了多个类,那么其寻找方式有两种,深度优先和广度优先(下方有讲)
## 继承例子
class Dad: "这是父类" name = "henry_Dad" def __init__(self,name): self.name = name print("父类") class Sun(Dad): pass print(Sun.name) # henry_Dad print(Dad.__dict__) # {\'__module__\': \'__main__\', # \'__doc__\': \'这是父类\', # \'name\': \'henry_Dad\', # \'__init__\': <function Dad.__init__ at 0x000001E8916CB8C8>, # \'__dict__\': <attribute \'__dict__\' of \'Dad\' objects>, # \'__weakref__\': <attribute \'__weakref__\' of \'Dad\' objects>} print(Sun.__dict__) # {\'__module__\': \'__main__\', \'__doc__\': None}
## 问题:
问:子类继承了父类的属性,子类自定义的属性与父类中的属性重名,那么就覆盖了父类?
答:不是覆盖,当子类调用一个数据属性或函数属性时,子类现在自己的类中找,若找不到则在父类中找,若找到则不在父类中找
什么时候用到继承:
1:当类之间有显著的不同,且较小的类为较大的类的组件时,用组合较好
2:当类之间具有很多相同的功能,提取这些功能作为基类,用继承比较好
## 父类:动物(吃,喝,玩,乐)
## 子类:小狗(汪汪叫)
## 子类:母鸡(喔喔叫)
## 继承具有的含义
1:继承基类的方法(尽量少用,会让类与类之间的耦合度增加,因遵循低耦合 高内聚),并且做出自己的改变或或扩展
在实践中,该继承的意义不大,甚至是有害的,因为它使基类和子类之间的耦合性加强了
2:声明某个子类兼容于某个基类,定义一个类的接口,子类继承接口类,并且实现接口中定义的方法
接口继承实质上要求“做出一个良好的抽象,这个抽象定义一个兼容的接口,使得外部的调用者无需知道具体的实现细节,可一视同仁的处理特定接口的所有对象”
## abc模块
在Python中本身不提供抽象类和接口机制,想要实现抽象类,可以利用abc模块,BAC为 Abstract Base Class
import abc class Animal(metaclass=abc.ABCMeta): @abc.abstractmethod # 声明该适配器,被声明的方法在本类中不用实现 def eat(self): pass @abc.abstractmethod def sleep(self): pass class Cat(Animal): def eat(self): pass def sleep(self): pass c = Cat() class Dog(Animal): def eat(self): pass d = Dog() # 当类Dog中没有实现方法sleep时,执行这步会报错(必须实现父类中的抽象方法)
## 深度优先 和 广度优先
深度优先遍历:对每一个可能的分支路径深入到无法深入为止,且每个节点只访问一次
广度优先遍历:又叫层次遍历,从上往下对每一层依次访问,在每一层中,从左往右(也可以从右往左)访问结点,访问完一层就进入下一层,直到没有结点可以访问为止。
## 在Python中如何实现继承
1:在Python中对于你定义的每个类,Python会计算出一个方法解析顺序列表(MRO),这个MRO列表就是一个简答的所有基类的线性顺序表
2:为了实现继承,Python会从MRO列表中从左到右开始查找基类,知道找到第一个匹配这个属性的类为止
# 2.1:子类会先于基类被检查
# 2.2:如果一个子类有多个基类,则根据它们在MRO列表中的顺序进行检查
# 2.3:如果对一个子类存在两个合法的基类,则选择第一个基类
## 新式类/经典类中的查找顺序
# Python 3 为新式类
# Python 2 为经典类和新式类
class C1: # 经典类,没有继承关系 pass class C2(C1): # 经典类,因为继承的C1为经典类 pass
class B1(object): # 经典类,Python3基类默认继承了object pass class B2(B1): # 新式类 pass
# 当为经典类时,多继承情况下,会按照深度优先查找 # 当为新式类时,多继承情况下,会按照广度优先的方法查找
## MRO示例
# 下图为继承关系样例图
## 新式类中的查找顺序
class A: def test(self): print("A") class B(A): def test(self): print("B") class C(B): def test(self): print("C") class D(A): def test(self): print("D") class E(D): def test(self): print("E") class F(C,E): def test(self): print("F") print(F.__mro__) # 经典类中无此方法 # (<class \'__main__.F\'>, <class \'__main__.C\'>, <class \'__main__.B\'>, <class \'__main__.E\'>, <class \'__main__.D\'>, <class \'__main__.A\'>, <class \'object\'>)
## 经典类中的查找顺序(Python中),经典类中无__mro__方法
class A: def test(self): print("A") class B(A): def test(self): print("B") class C(B): def test(self): print("C") class D(A): def test(self): print("D") class E(D): def test(self): print("E") class F(C,E): def test(self): print("F") f = F() f.test() # F->D->B->A->E->C
## super方法的使用
# 方式一,在子类中直接写上基类的名称
class Animal(): def __init__(self,name,speed,appetite,master): self.name = name self.speed = speed self.appetite = appetite self.master = master def walk(self): print("开始遛了") class Dog(Animal): def __init__(self,name,speed,appetite,master,size): # 自有属性为size Animal.__init__(self,name,speed,appetite,master) self.size = size def Walk_the_dog(self): Animal.walk(self) print("开始遛狗啦") d = Dog("small_yellow","10km/h","十碗","henry",30) d.Walk_the_dog() # 输出 开始遛了 开始遛狗啦
# 以上的方法存在缺陷,当父类名修改时,子类中涉及父类名称的也得进行修改,扩展性差
# 方式二,利用super
class Animal: def __init__(self,name,speed,appetite,master): self.name = name self.speed = speed self.appetite = appetite self.master = master def walk(self): print("开始遛了") class Dog(Animal): def __init__(self,name,speed,appetite,master,size): # 自有属性为size # Animal.__init__(self,name,speed,appetite,master) super().__init__(name, speed, appetite, master) self.size = size def Walk_the_dog(self): # Animal.walk(self) super(Dog,self).walk() print("开始遛狗啦") d = Dog("small_yellow","10km/h","十碗","henry",30) d.Walk_the_dog() # 输出 开始遛了 开始遛狗啦
....
多态:由不同的类实例化得到的对象,这些对象调用同一个方法,执行不同的逻辑
多态反应一种在执行时候的状态
>>> str1 = "abc" >>> list1 = [1,1,2,3] >>> str1.__len__() 3 >>> list1.__len__() 4 >>> # 其中 str1和list1为不同的对象,都去调用方法__len__(),但执行不同的逻辑来得到结构
## 多态例子
class H20: def __init__(self,h2o_type,temperature): self.h2o_type = h2o_type # H2O类型 self.temperature = temperature # 温度 def translate(self): if self.temperature > 100: print("当前的温度为:%s 大于100,变成了%s" %(self.temperature,self.h2o_type)) elif self.temperature > 0 & self.temperature < 100: print("当前的温度为:%s 位于0和100之间,变成了%s" % (self.temperature, self.h2o_type)) elif self.temperature < 0: print("当前的温度为:%s 小于0,变成了%s" % (self.temperature, self.h2o_type)) class Water(H20): pass class Ice(H20): pass class Stream(H20): pass w = Water("水",20) # 由不同的类实例化出不同的对象 i = Ice("冰",-20) # 由不同的类实例化出不同的对象 s = Stream("蒸汽",111) # 由不同的类实例化出不同的对象 w.translate() # 调用同一个方法(执行不同的逻辑) i.translate() # 调用同一个方法(执行不同的逻辑) s.translate() # 调用同一个方法(执行不同的逻辑) # 当前的温度为:20 位于0和100之间,变成了水 当前的温度为:-20 小于0,变成了冰 当前的温度为:111 大于100,变成了蒸汽
# 注
注:多态实际上是依附于继承的两种含义,“改变”和“扩展”本身就意味着必须有着一种机制去实现改变和扩展,如果没有多态,两种含义则就不可能实现
# 多态实际上是继承的实现细节,让多态与封装,继承这两个并列,显然不符合逻辑
...
封装1 普通的封装形式
# 第一层封装,类就是麻袋,这本身就是一种封装
封装2
# 第二层封装,类中定义私有的,只在类的内部进行使用,而在外部无法进行使用 _ __ (使用单下划线和双下划线)
# 当属性以单下划线 _ 开头,那么它就是属于内部的属性,不能被外部调用(当你发现有用单下划线开头的属性,则不该调用该属性)
# 这只是一种约定,Python并不会真正阻止你访问私有的属性,外部还是能调用的
# 当属性一双下划线开头 __ ,Python会自动做一个重命名的操作,
# 外部不能访问 __star ,内部可以进行直接访问 __star
class People: __star = "earth" # __star被 Python重命名为 _People__star
例子
class Dog: def __init__(self,name,speed,appetite,master): self.name = name self.speed = speed self.appetite = appetite self.master = master def walk(self): print("开始遛%s" %self.name) d = Dog("small_yellow","10km/h","十碗","henry")
# 当调用其属性时 print(d.name,d.speed,d.appetite,d.master) # small_yellow 10km/h 十碗 henry
# 当调用其方法时 d.walk() # 开始遛small_yellow
# 当将类中的属性隐藏起来
class Dog: def __init__(self,name,speed,appetite,master): self.__name = name self.__speed = speed self.__appetite = appetite self.__master = master def walk(self): print("开始遛%s" %self.__name) d = Dog("small_yellow","10km/h","十碗","henry")
# 当调用其属性时 报错 print(d.name) # AttributeError: \'Dog\' object has no attribute \'name\'
# 同样报错 print(d.__name) # AttributeError: \'Dog\' object has no attribute \'__name\'
# 当调用其方法时,方法还是可以进行调用 d.walk() # 开始遛small_yellow
## 进行隐藏后如何进行访问
class Dog: def __init__(self,name,speed,appetite,master): self.__name = name self.__speed = speed self.__appetite = appetite self.__master = master def walk(self): print("开始遛%s" %self.__name) d = Dog("small_yellow","10km/h","十碗","henry")
print(d.__dict__) # {\'_Dog__name\': \'small_yellow\', \'_Dog__speed\': \'10km/h\', \'_Dog__appetite\': \'十碗\', \'_Dog__master\': \'henry\'} # 在Python中,当用"__属性名"进行命名时,Python自动将其隐藏的属性的命名格式进行了修改,修改成了“_类名__属性名”
# 当然也可以利用 _类名__属性名 来访问对应的属性 print(d._Dog__name) # small_yellow
...
当在类中添加一个属性时,如果将属性直接暴露在外,让对象可以随意的调用,这就会让属性可以随意的更改
## 实例化出的对象可以随意的获取和修改 name
class Dog: def __init__(self,name): self.name = name d =Dog("藏獒") print(d.name) # 藏獒 d.name = "中华田园犬" print(d.name) # 中华田园犬
## 在类中添加方法 get_name()来获取名字,添加set_name()来写入名字(现在就不能随心所欲设置名字了)
class Dog: def set_name(self,name): if len(name) > 10: print("名字的长度超过10个字符") else: self.name = name def get_name(self): return self.name d = Dog() d.set_name("中华田园犬") print(d.get_name()) # 中华田园犬
d1 = Dog()
d1.set_name("我是超级可爱的中华田园犬") # 名字的长度超过10个字符
## @property的作用就是负责将一个方法变成一个属性调用
class Dog: def __init__(self,val): self.__NAME = val # 将数据隐藏起来 @property def name(self): return self.__NAME # obj.name访问的是self.__NAME @name.setter def name(self,value): if len(value) > 10: raise TypeError(\'名字的长度超过10个字符\') self.__NAME = value d = Dog("中华田园犬") d.name = "藏獒" # 实际转化为 set_name("藏獒") print(d.name) # 实际转化为 get_name
## 可以自定义只读属性,只定以getter方法
# 由getter方法和setter方法,为可读可写 class Dog: def __init__(self,val): self.__NAME = val @property def name(self): return self.__NAME @name.setter def name(self,value): self.__NAME = value
# 只有getter方法,为只读 class Cat: def __init__(self,val): self.__NAME = val @property def name(self): return self.__NAME
...
以上是关于Python_基础_(面向对象三大特性)的主要内容,如果未能解决你的问题,请参考以下文章