多态:一个事物有多种形态(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()