面向对象三大特性:封装、继承、多态

封装

概念:

  将一些属性和相关方法封装在一个对象中,对外隐藏内部具体实现细节。

  内部实现,外界不需要关心,外界只需要根据”内部提供的接口“去使用就可以。

好处:

  1. 使用起来更加方便:因为已经把很多相关的功能,封装成一个整体,类似于像外界提供一个工具箱,针对于不同的场景,使用不同的工具箱就可以;

  2. 保证数据的安全:针对于安全级别高的数据,可以设置成”私有“,可以控制数据为只读(外界无法修改),也可以拦截数据的写操作(进行数据校验和过滤);

  3. 利于代码维护:如果后期,功能代码需要维护,则直接修改这个类内部代码即可;只要保证接口名称不变,外界不需要做任何代码修改。

继承

概念:

  现实中的”继承“:子女继承父母的”财产资源“

  编程中的”继承“:一个类”拥有“另外一个类的”资源“的方式之一;”拥有“:并不是资源的复制,编程双份资源,而是,资源的”使用权“;”资源“:指"非私有的"属性和方法。例如Dog类继承自Animal类

  被继承的类:父类,基类,超类

  继承的类:子类,派生类

目的:

  方便资源重用

分类:

  单继承:

  • 概念:仅仅继承了一个父类。

  • class Father:
        pass
    
    class Chile(Father):
        pass
    

  多继承:

  • 概念:继承了多个父类

  • class Father:
        pass
    
    class Mother:
        pass
    
    class Child(Father, Mother):
        pass
    

    查看父类的Child.__bases__

type和object区分

type,object关系

继承下的影响:

资源的继承

  明确:在Python中,继承是指资源的使用权,所以,测试某个资源能否被继承,其实就是测试在子类当中,能不能访问到父类当中的资源

  结论:Python中的继承并不是复制,只是使用资源。子类除不能访问父类的私有的属性和私有的方法外,其余均可继承(公有属性/方法,受保护属性/方法,内置方法)。

class Animal:
    # 属性和方法
    # 设置不同权限的属性和方法,继承当中进行测试
    # 在子类当中能否访问到这些资源
    a = 1 # 公共属性
    _b = 2 # 保护属性
    __c = 3 # 私有属性

    def t1(self): # 公共方法
        print('t1')

    def _t2(self): # 保护方法
        print('t2')

    def __t3(self): # 私有方法
        print('t3')

    def __init__(self): #内置
        print("init, Animal")

class Person(Animal):
    def test(self):
        print(id(self.a))
        print(self.a)
        print(self._b)
        # print(self.__c)

        self.t1()
        self._t2()
        # self.__t3()

        self.__init__()

p = Person()
p.test()

print(id(Animal.a))
Animal.a = 666
p.test()
资源的使用
  1. 继承的几种形态:
    • 单继承链
    • 无重叠的多继承链
    • 有重叠的多继承链
  2. 几种形态应该遵循的规则
    • 单继承链
      • 从下往上(A.age 先从A自身查找,若无,B查找,若无,C查找)A==>B==>C
    • 无重叠的多继承链
      • 单调原则,优先先到左侧查找。(A.age先从A自身查找,若无,B中查找,如无,D中查找,若无,C中查找,如无,E中查找)A==>B==>D==>C==>E
    • 有重叠的多继承链
      • 从下到上,单调原则,优先上一级左,右,然后上上级。(A.age先从A自身查找,若无,B中查找,如无,C中查找,如无,D中查找)A==>B==>C==>D

继承分类

步骤

class B:
    age = 3

class A(B):
    pass

print(A.age)
A.age = 9
print(B.age)
print(A.age)
print(A.__dict__)
print(B.__dict__)
# 单继承链
class C:
    pass

class B(C):
    pass

class A(B):
    pass

print(A.mro())

# 无重叠的多继承链

class E:
    pass

class D:
    pass

class C(E):
    pass

class B(D):
    pass

class A(B, C):
    pass

print(A.mro())

# 菱形结构
class D:
    pass

class C(D):
    pass

class B(D):
    pass

class A(B, C):
    pass


print(A.mro())
  1. 针对几种标准原则的方案演化:

    inspect模块inspect.getmro(A)

    B.__mro__``B.mro()查找资源查找顺序

Python2.2之前

  仅仅存在经典类。MRO原则(深度优先–从左往右)。
  问题:”有重叠的多继承“中,违背了”重写可用原则“A==>B==>D==>C

Python2.2:产生了新式类,MRO原则:

  经典类: 深度优先(从左到右)
  新式类:

  1. 在深度优先(从左到右)的算法基础之上,优化了一部分:如果产生重复元素,会保留最后一个,并且更尊重基类出现的先后顺序。注意:并不是广度优先算法。
  2. 问题:无法检测出有问题的继承;有可能还会违背”局部优先“原则。

Python2.3-2.7

  新式类经典类并存。MRO原则:经典类:深度优先(从左到右),新式类:C3算法。

Python3.x之后

  MRO原则–新式类–C3算法

概念补充

   MRO(Method Resolution Order):方法解析顺序

  深度优先():沿着一个继承链,尽可能的往深了去找。
  具体步骤:

  1. 把根节点压入栈中。
  2. 每次从栈中弹出一个元素,搜索所有在它下一级的元素把这些元素压入栈中(发现已经被处理,则略过)。
  3. 重复第二个步骤到结束为止。

  广度优先(队列):沿着继承链,尽可能往宽了去找。

具体算法步骤:

  1. 把根节点放到队列的末尾。
  2. 每次从队列的头部去除一个元素,查看这个元素所有的下一级元素,把它们放到队列的末尾(发现已经被处理,则略过)。
  3. 重复上面步骤。

  C3算法:

  真正步骤:

  两个公式:
  L(object)=[object]
  L(子类(父类1,父类2)=[子类]+merge(L(父类1),L(父类2),[父类1,父类2])),

  注意,+代表合并列表,merge算法:

在这里插入图片描述
  类似拓扑排序,但并不是!如下图错误类关系,拓扑结构无法认证其错误问题。

错误类关系

资源的覆盖:相同属性,子类覆盖父类

  根据优先级在优先级较高内的资源写相同的资源,形成一种资源被覆盖的现象。

分类:

  1. 属性的覆盖。
  2. 方法重写。

  原理: 在MRO的资源检索链当中,优先级比较高的类写了一个和优先级比较低的类一样的一个资源(属性或方法,而摒弃优先级比较低的资源,造成”覆盖“的假象。

  注意事项:当调用优先级比较高的资源时,注意self的变化。谁调用,传递谁对象。

# 属性的覆盖
class E:
    pass

class D:
    age = 1

class C(E):
    pass

class B(D):
    age = 2

class A(B, C):
    pass

print(A.age)
print(A.mro())

# 方法的重写
class E:
    pass

class D:
    def test(self):
        print("D")

class C(E):
    pass

class B(D):
    def test(self):
        print("B")

class A(B, C):
    pass

print(A().test())
print(A().test)
print(A.mro())
# A().test 表明 调用A,self为A。
资源的累加

  概念:在一个类的基础上,增加一些额外的资源

  子类相比父类,多一些自己特有的资源

  在被覆盖的方法基础之上,新增内容。

class B:
    a1 = 1
    
class A(B):
    a2 = 2

print(A.a1)
print(A.a2)
class B:
    a = 1

    def __init__(self):
        self.b = 2

    def test(self):
        print('t')

    @classmethod
    def test1(cls):
        print('t1')

    @staticmethod
    def test2():
        print('t2')

class A(B):
    c = 3

    # def __init__(self): # 在存在自己的初始化函数__init__时,不会调用父类的初始化函数
    #     self.e = '666' # 解决办法:1: 直接复制父类的内容到子类初始化函数内
                        # 2.调用:通过类名;通过super

    def __init__(self):
        B.__init__(self)
        self.e = '666'

    def ttest(self):
        print('t')

    @classmethod
    def ttest1(cls):
        print('t1')

    @staticmethod
    def ttest2():
        print('t2')

    pass

p = A()
print(p.a)
print(p.b)
p.test()
p.test1()
p.test2()
print(p.c)
p.ttest()
p.ttest1()
p.ttest2()
p.d = 'xxx'
print(p.d)
print(p.e)

  存在问题:

  子类在存在自己的初始化函数__init__时,不会调用父类的初始化函数,造成父类的初始化函数参数无法及时返回到子类中。

  解决办法:

  1. 直接复制父类的内容到子类初始化函数内
  2. 调用:
    • 通过类名(上述例子)
    • 通过super函数(建议使用)
# 创建菱形结构

class D(object):
    def __init__(self):
        print('d')

class B(D):
    def __init__(self):
        D.__init__(self)
        print('d')

class C(D):
    def __init__(self):
        D.__init__(self)
        print('c')

class A(B, C):
    def __init__(self):
        B.__init__(self)
        C.__init__(self)
        print('a')

B()
C()
A()

  调用类这种方法会在A类中产生重复调用的情况,因此也存在着问题,但在经典类中只能使用这种方法。在新式类中可以采用super方法。

在低优先级类的方法中,通过"super"调用高优先级类的方法。

  概念:是一个类,只有在新式类中有效。

  作用:

  • 起着代理的作用,帮我们完成以下任务
  • 沿着MRO链条,找到下一级节点,去调用对应的方法。注意不是严格的父类关系(菱形关系:A==>B==>C==>D,继承关系:A==>B,A==>C,B==>D,C==>D

  问题:

  • 沿着谁的MRO链条?
  • 找谁的下一个节点?
  • 如何应对类方法,静态方法以及实例方法的传参问题?

  语法原理:

  * super(参数1[,参数2])

  * 工作原理:

def super(cls, inst):
    mro = inst.__class__.mro()
    retun mro[mro.index(cls)+1]

  * 问题解决:

  • 沿着谁的MRO链条?答:参数2
  • 找谁的下一个节点?答:参数1
  • 如何应对类方法,静态方法以及实例方法的传参问题?
    答:1.使用参数; 2.进行调用

常用具体语法形式:

# 适用于新式类
# Python2.2+
super(type,obj)->bound super object
# obj当作另外一个实例方法里的参数传递,type是类
super(type, type2)->bound super object
# 调用其他类的类方法,传递给类方法的第一个参数cls
# python 3+
super() #自动输入

  通过super调用父类中的方法,注意:第一个参数是类函数,第二个参数是指传递的参数。第一个参数指查找哪一个类的下一个节点,使用第二个参数的MRO链条去调用后续的方法。

class B:
    a = 1

    def __init__(self):
        self.b = 2

    def test(self):
        print('t')

    @classmethod
    def test1(cls):
        print('t1')

    @staticmethod
    def test2():
        print('t2')

class A(B):
    c = 3

    def __init__(self):
        super(A, self).__init__()
        self.e = '666'

    def ttest(self):
        print('t')

    @classmethod
    def ttest1(cls):
        super(A, cls).test1() # cls或A 都可以,cls也在调用A类。 注意第一个参数为类名称,第二参数为传递参数
        print('t1')

    @staticmethod
    def ttest2():
        print('t2')

    pass

p = A()
print(p.a)
print(p.b)
p.test()
p.test1()
p.test2()
print(p.c)
p.ttest()
p.ttest1()
p.ttest2()
p.d = 'xxx'
print(p.d)
print(p.e)
print(p.__dict__)
p.ttest1()

菱形问题解决

class D(object):
    def __init__(self):
        print('d')

class B(D):
    def __init__(self):
        super(B, self).__init__() # 切记不要用self.__class__代替B,因为self不一定是B中的类,有可能是子类传递过来的self 
        print('b')

class C(D):
    def __init__(self):
        super(C, self).__init__()
        print('c')

class A(B, C):
    def __init__(self):
        super().__init__()
        print('a')

# B()
# C()
A()

注意事项:

  • 切记不要用super(self.__class__,self)代替cls,因为self不一定是cls中的类,有可能是子类传递过来的self
  • 不要通过类名和super混合使用调用资源。

多态

概念:

  一个类,所延伸的多种形态;

  调用时的多种形态:在继承的前提下;使用不同的子类,调用父类的同一个方法,产生不同的功能

class Animals:
    def jiao(self):
        pass

class Dog(Animals):
    def jiao(self):
        print('Wangwang')

class Cat(Animals):
    def jiao(self):
        print('Miaomiao')

def test(obj):
    obj.jiao()

d = Dog()
c = Cat()
test(d)
test(c)

多态在Python中的体现

  1. 鸭子类型:
    • 动态类型的一种风格
    • 只要一个对象,会走,会游泳,会叫;那么它就可以当作一个鸭子进行处理
    • 关注点在于对象的“行为和属性”,而非对象的“类型”
  2. 所以,在Python当中,没有真正意义上的多态;也不需要多态

补充

抽象类

  一个抽象出来的类,并不是某一个具化的类

  不能直接创建实例的类,创建会报错

抽象方法

  抽象出来的一个方法(Animal)

  不具备具体实现,不能直接调用;子类不是先会报错(jiao)

Python中的实现

  1. 无法直接支持,需要借助一个模块:import abc
  2. 设置类的元类为abc.ABCMeta
  3. 使用装饰器修饰抽象方法@abc.abcstractmethod
import abc

class Animals(object, metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def jiao(self):
        pass

    @abc.abstractclassmethod
    def test(cls):
        print('cls')

class Dog(Animals):
    def jiao(self):
        print('Wangwang')

    @classmethod
    def test(cls):
        print('Dogcls')
    pass

class Cat(Animals):
    def jiao(self):
        print('Miaomiao')

def test(obj):
    obj.jiao()

d = Dog()
# c = Cat()
test(d)
# test(c)
d.jiao()
d.test()

  注意,在类中使用abc模块中的方法、静态方法和类方法时,声明之后必须要在运行的子类中也有相应的方法,否则无法运行。

Logo

瓜分20万奖金 获得内推名额 丰厚实物奖励 易参与易上手

更多推荐