• Python进阶---面向对象第二弹


    python类的继承原理

    一、类的继承顺序

     

    class A(object):
    def test(self):
    print('from A')
    pass
    class B(A):
    # def test(self):
    # print('from B')
    pass
    class C(A):
    # def test(self):
    # print('from C')
    pass
    class D(A):
    # def test(self):
    # print('from D')
    pass
    class E(B):
    # def test(self):
    # print('from E')
    pass
    class F(C):
    # def test(self):
    # print('from F')
    pass
    class G(D):
    # def test(self):
    # print('from G')
    pass
    class H(E,F,G):
    # def test(self):
    # print('from H')
    pass
    h=H()
    # h.test=1
    # print h.__dict__
    #新式类的在这中继承结构下,属性的查找关系
    # h.test()

    # H->E->B->F->C-G-D-A 广度优先
    #mro
    print(H.mro())

    二、类的继承原理

    python到底是如何实现继承的,对于你定义的每一个类,python会计算出一个方法解析顺序(MRO)列表,这个MRO列表就是一个简单的所有基类的线性顺序列表,例如

    >>> F.mro() #等同于F.__mro__
    [<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]

    为了实现继承,python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。
    而这个MRO列表的构造是通过一个C3线性化算法来实现的。我们不去深究这个算法的数学原理,它实际上就是合并所有父类的MRO列表并遵循如下三条准则:
    1.子类会先于父类被检查
    2.多个父类会根据它们在列表中的顺序被检查
    3.如果对下一个类存在两个合法的选择,选择第一个父类

    三、子类调用父类的方法

    两种方式 :一个是直接指明道姓的调用父类的函数属性还有一个是调用父类的__init__的功能,实际上用的是绑定方法。

    class People:
    # def __init__(self,name,age,sex):
    # self.name=name
    # self.age=age
    # self.sex=sex
    # def foo(self):
    # print('from parent')
    #
    # class Teacher(People):
    # def __init__(self,name,age,sex,salary,level):
    # # People.__init__(self,name,age,sex) #指名道姓地调用People类的__init__函数
    #
    # #在python3中
    # super().__init__(name,age,sex) #调用父类的__init__的功能,实际上用的是绑定方法
    #
    # #在python2中
    # # super(Teacher,self).__init__(name,age,sex)
    #
    #
    # self.salary=salary
    # self.level=level
    # def foo(self):
    # super().foo()
    # print('from child')
    #
    #
    # t=Teacher('egon',18,'male',3000,10)
    # # print(t.name,t.age,t.sex,t.salary,t.level)
    # t.foo()

    注意注意注意:使用super调用的所有属性,都是从MRO列表当前的位置往后找,千万不要通过看代码去找继承关系,一定要看MRO列表

    四封装

    第一个层面的封装(什么都不用做):创建类和对象会分别创建二者的名称空间,我们只能用类名.或者obj.的方式去访问里面的名字,这本身就是一种封装

    注意:对于这一层面的封装(隐藏),类名.和实例名.就是访问隐藏属性的接口

    第二个层面的封装:类中把某些属性和方法隐藏起来(或者说定义成私有的),只在类的内部使用、外部无法访问,或者留下少量接口(函数)供外部访问。

    在python中用双下划线的方式实现隐藏属性(设置成私有的)

    类中所有双下划线开头的名称如__x都会自动变形成:_类名__x的形式:

    class A:
        __N=0 #类的数据属性就应该是共享的,但是语法上是可以把类的数据属性设置成私有的如__N,会变形为_A__N
        def __init__(self):
            self.__X=10 #变形为self._A__X
        def __foo(self): #变形为_A__foo
            print('from A')
        def bar(self):
            self.__foo() #只有在类内部才可以通过__foo的形式访问到.

     
    #封装数据
    #封装功能
    class Teacher:
    __school='oldboy' #_Teacher__school
    def __init__(self,name,salary):
    self.name=name
    self.__salary=salary #self._Teacher__salary
     
    def __foo(self):
    print('====>')
    t=Teacher('egon',3000)
     
    # print(t.__school)
    # print(Teacher.__dict__)
     
    # t.foo()
    # t._Teacher__foo()
     
    # print(t.salary)
    # print(t.__salary)
    # print(t.__dict__)
    #
    # print(t._Teacher__salary)
     
    #这种变形操作只在定义阶段发生
    # Teacher.__N=111111
    # print(Teacher.__dict__)
    #
    #
    # t.__x=1
    # print(t.__dict__)
     
    #在类的外部,无法直接使用变形的属性,但是在类的内部可以直接使用
    class Teacher:
    __school='oldboy' #_Teacher__school='oldboy'
    def __init__(self,name,salary):
    self.name=name
    self.__salary=salary #self._Teacher__salary=salary
     
    def foo(self):
    print('====>',self.__salary)
    # print('====>',self._Teacher__salary)
    t=Teacher('egon',3000)
     
    print(t.__salary)
    t.foo()

    这种自动变形的特点:

    1.类中定义的__x只能在内部使用,如self.__x,引用的就是变形的结果

    2.这种变形其实正是针对外部的变形,在外部是无法通过__x这个名字访问到的。

    2.在子类定义的__x不会覆盖在父类定义的__x,因为子类中变形成了:_子类名__x,而父类中变形成了:_父类名__x,即双下滑线开头的属性在继承给子类时,子类是无法覆盖的。

    注意:对于这一层面的封装(隐藏),我们需要在类中定义一个函数(接口函数)在它内部访问被隐藏的属性,然后外部就可以使用了

    五、类的特性(property

    1 什么是特性property

    property是一种特殊的属性,访问它时会执行一段功能(函数)然后返回值

    2 为什么要用property

    将一个类的函数定义成特性以后,对象再去使用的时候obj.name,根本无法察觉自己的name是执行了一个函数然后计算出来的,这种特性的使用方式遵循了统一访问的原则

    上代码:

    class People:
    def __init__(self,name,age,sex,height,weight,permission=False):
    self.__name=name
    self.__age=age
    self.__sex=sex
    self.__height=height
    self.__weight=weight
    self.permission=permission

    @property
    def name(self):
    return self.__name
    @name.setter
    def name(self,val):
    if not isinstance(val,str):
    raise TypeError('名字必须为字符串!')
    self.__name=val
    @name.deleter
    def name(self):
    if not self.permission:
    raise PermissionError('权限不够!')
    del self.__name
    @property
    def bmi(self):
    return self.__weight/(self.__height**2)
    def tell_info(self):
    print('''
    ---------%s info------------
    Name:%s
    Age:%s
    Sex:%s
    Height:%sM
    Weight:%sKG
    ---------------------------------
    '''%(self.__name,self.__name,self.__age,self.__sex,self.__height,self.__weight))
    niubin=People('niubin',20,'male',1.70,68)
    niubin.tell_info()
    print(niubin.name)
    print(niubin.bmi)
  • 相关阅读:
    隐语义模型LFM
    基于内容的推荐、协同过滤
    评定标准
    函数式模型示例
    函数式模型概述
    序贯模型
    seq2seq
    链队列
    顺序栈
    线性表的静态链表存储结构
  • 原文地址:https://www.cnblogs.com/niubin/p/7123903.html
Copyright © 2020-2023  润新知