面向对象--多态、封装
多态:一个事物有多种形态(Python天生就支持多态)
# 动物有多种形态
import abc
# 定义规则--接口类或抽象类
# 若子类不定义一样的方法主动抛出异常
class Animal(metaclass=abc.ABCMeta):
@abc.abstractmethod
def talk(self):pass
class Cat(Animal): # 动物的形态之一:猫
def talk(self):
print('cat')
class Dog(Animal): # 动物的形态之二:狗
def talk(self):
print('dog')
class Pig(Animal): # 动物的形态之三:猪
def talk(self):
print('pig')
多态性:多态性是指在不考虑实例类型的情况下使用实例
多态动态绑定:在继承的背景下使用,有时也称为多态性
在面向对象方法中一般是这样表述多态性:
向不同的对象发送同一条消息(!!!obj.func():是调用了obj的方法func,又称为向obj发送了一条消息func),不同的对象在接收时会产生不同的行为(即方法)。
也就是说,每个对象可以用自己的方式去响应共同的消息。所谓消息,就是调用函数,不同的行为就是指不同的实现,即执行不同的函数。
比如:老师.下课铃响了(),学生.下课铃响了(),老师执行的是下班操作,学生执行的是放学操作,虽然二者消息一样,但是执行的效果不同
cat=Cat() dog=Dog() pig=Pig() #cat、dog、pig都是动物,只要是动物肯定有talk方法 #于是我们可以不用考虑它们三者的具体是什么类型,而直接使用 cat.talk() dog.talk() pig.talk() #更进一步,我们可以定义一个统一的接口来使用 def func(obj): obj.talk()
鸭子类型:功能或者方法相似,但却没有关联的(如:list,tuple 都能调用index方法,但是删除了list的index并不会影响tuple使用index.)
class List: def __len__(self):pass class Tuple: def __len__(self):pass def len(l_t): return l_t.__len__() l = List() len(l)
# 鸭子类型 --- 相似,但没有关联关系 # Python不崇尚根据继承所得来的相似; # 我只自己实现我自己的代码就可以了; # 如果两个类刚好相似,并不产生父类的子类的兄弟关系,而是鸭子类型 # list tuple 这种相似,是自己写代码的时候约束的,而不是通过父类约束的 # 优点 :松耦合 每个相似的类之间没影响 # 缺点 :太随意了,只能靠自觉
封装
封装:隐藏对象的属性和实现细节,仅对外提供公共访问方式。
优点:1、将变化隔离;2、便于使用;3、提高复用性;4、提高安全性;
封装原则:1、将不需要对外提供的内容都隐藏起来;2、把属性都隐藏,提供公共方法对其访问;
封装类型:私有静态属性、私有方法、私有属性
封装使用:在静态属性名、方法名或者属性名的左边加上双下划线‘‘__’,即可转为私有.
class Login: __key = 123 # 定义了一个私有静态属性 def __init__(self,name,pw): self.name = name self.__pw = pw # 定义了一个私有属性,外部无法调用该属性,只能内部调用 def __get_pw(self): # 定义了一个私有方法 print(self.__dict__) # 打印查看self里的数据内容 return self.__pw # 调用私有属性 # 显示pw def login(self): return self.__get_pw() # 调用私有方法 jerry = Login('jerry','123') print(jerry.name) print(jerry._Login__pw) # 无法使用jerry.pw调用 print(jerry.login())
注意事项:
1.这种机制也并没有真正意义上限制我们从外部直接访问属性,知道了类名和属性名就可以拼出名字:_类名__属性,然后就可以访问了,如a._A__N 2.变形的过程只在类的内部生效,在定义后的赋值操作,不会变形 3.在继承中,父类如果不想让子类覆盖自己的方法,可以将方法定义为私有的
# 正常情况 class A: def fa(self): print('from A') def test(self): self.fa() # 在以调用的类为节点,所以会先找B中是否有fa方法,没有再去找A的 class B(A): def fa(self): print('from B') b = B() b.test() # 把fa定义成私有的,即__fa class A: def __fa(self): # 在定义时就变形为_A__fa print('from A') def test(self): self.__fa() # 只会与自己所在的类为准,即调用_A__fa class B(A): def __fa(self): print('from B') b = B() b.test()
封装与扩展性
封装在于明确区分内外,使得类实现者可以修改封装内的东西而不影响外部调用者的代码;而外部使用用者只知道一个接口(函数),只要接口(函数)名、参数不变,使用者的代码永远无需改变。
这就提供一个良好的合作基础——或者说,只要接口这个基础约定不变,则代码改变不足为虑。
#类的设计者 class Room: def __init__(self,name,owner,width,length,high): self.name=name self.owner=owner self.__width=width self.__length=length self.__high=high def tell_area(self): #对外提供的接口,隐藏了内部的实现细节,此时我们想求的是面积 return self.__width * self.__length #使用者 >>> r1=Room('卧室','egon',20,20,20) >>> r1.tell_area() #使用者调用接口tell_area #类的设计者,轻松的扩展了功能,而类的使用者完全不需要改变自己的代码 class Room: def __init__(self,name,owner,width,length,high): self.name=name self.owner=owner self.__width=width self.__length=length self.__high=high def tell_area(self): #对外提供的接口,隐藏内部实现,此时我们想求的是体积,内部逻辑变了,只需求修该下列一行就可以很简答的实现,而且外部调用感知不到,仍然使用该方法,但是功能已经变了 return self.__width * self.__length * self.__high #对于仍然在使用tell_area接口的人来说,根本无需改动自己的代码,就可以用上新功能 >>> r1.tell_area()
封装的注意事项
# 父类的私有属性能否被子类调 # 不能调用,只要父类设置了私有的静态属性、方法、属性都不能被子类调用 # 私有属性只能在一个类的内部调用
class Foo: __key = '123' class Son(Foo): print(Foo.__key) ret = Son() print(ret)
会用到私有的这个概念的场景 # 隐藏起一个属性,不想让类的外部调用 # 保护这个属性,不想让属性随意被改变 # 保护这个属性,不被子类继承
# 可以随意修改name class Room: def __init__(self,name,length,width): self.name = name self.__length = length self.__width = width def area(self): return self.__length*self.__width Jerry = Room('House',10,10) print(Jerry.area()) # 不得随意修改name class Room: def __init__(self,name,length,width): self.__name = name self.__length = length self.__width = width def get_name(self): return self.__name def set_name(self,newName): if type(newName) is str and newName.isdigit() == False: self.__name = newName else: print('不合法的姓名') def area(self): return self.__length*self.__width Jerry = Room('House',10,10) print(Jerry.area()) Jerry.set_name('Tom') print(Jerry.get_name()) Jerry.set_name('2')
浙公网安备 33010602011771号