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