面向对象:抽象类,继承的实现原理,封装

Posted 葫芦七娃

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了面向对象:抽象类,继承的实现原理,封装相关的知识,希望对你有一定的参考价值。

抽象类:

本身不能被实例化,也不应该被实例化,他的作用就是定义标准,不用具体的实现

继承的实现原理:

注:新式类可以使用print(<class_name>.mro())来查询,经典类无法使用

如下继承关系

根据上图代码示例:
class A:
    def test(self):
        print(\'from A\')
    pass
class B(A):
    def test(self):
        print(\'from B\')
    pass
class C:
    def test(self):
        print(\'from C\')
    pass
class D:
    def test(self):
        print(\'from D\')
    pass
class E(B,C,D):
    def test(self):
        print(\'from E\')
    pass

e = E()
e.test()

执行结果:
默认这样执行,会显示"from E" ,因为调用一个方法会,先从自己的类中查询,下面我们依次注释掉下列类的test:
把E类的test注释掉:结果显示"from B" ,
把B类的test注释掉:结果显示"from A" ,
把A类的test注释掉:结果显示"from C" ,
把C类的test注释掉:结果显示"from D" ,
把D类的test注释掉:结果显示"\'E\' object has no attribute \'test\'"
根据上面的结果过程:在Python3中,当继承多个父类时且父类没有共同的父类时,这时属性的查询顺序是(深度优先)
上面的属性查询顺序总结:E-->B-->A-->C-->D
这个可能不是很有说服力,那么我们在新建一个F类,让C类继承F类,这时属性的查询顺序如下(仅列出结果):
属性查询顺序:E-->B-->A-->C-->F-->D

  

如果继承多个附类且父类有共同的父类时:

根据上图代码示例:
class A:
    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:
    def test(self):
        print(\'from D\')
    pass
class E(B,C,D):
    def test(self):
        print(\'from E\')
    pass

e = E()
e.test()

执行结果:
默认执行:显示"from E"
把E类的test注释掉:结果显示"from B"
把B类的test注释掉:结果显示"from C"
把C类的test注释掉:结果显示"from A"
把A类的test注释掉:结果显示"from D"
把D类的test注释掉:结果显示"\'E\' object has no attribute \'test\'"
根据上面测试的结果:得出结论在Python3中,当继承多个父类且父类还有共同的父类时,这时属性查找是(广度优先)
上面的属性查找顺序总结:E-->B-->C-->A-->D

  

注:在Python2中因为分新式类和经典类的区别,所以跟Python3有些许不同。

如下图在Python2中:

  新式类:同Python3中一样,是广度优先。所以属性查找顺序是:E-->B-->C-->A-->D

  经典类:和新式类恰恰相反,是深度优先。所以属性查找顺序是:E-->B-->A-->C-->D

子类调用父类的方法:

super():内置函数,使用绑定方法调用父类的方法。(仅支持新式类)

注:在Python2中需要写成:super(S1,self).__init__(name,age)   #S1 为子类的名字。

class Parent:
    def __init__(self,name,age):
        self.name = name
        self.age = age

class D:
    def walk(self):
        return (\'from D\')

class S1(Parent,D):
    def __init__(self,name,age,job,school):
        super().__init__(name,age)   #因为使用绑定方法调用父类的方法,所以不需要传递self
        self.job = job
        self.school = school
        super().walk()
t = S1(\'egon\',73,\'teach\',\'oldboy\')

print(t.name,t.age,t.walk())

执行结果:
D:\\Python\\Python36-32\\python.exe E:/Python/DAY-20/day20.py
egon 73 from D

Process finished with exit code 0

  

封装:

  注:Python中没有真正的隐藏,仅仅是语法上做了些操作。

  方法:

    在想要封装的变量或者函数名前加上"__"两个下划线,就可以。

    加上两个下划线之后,只有在类的定义阶段或者对象的定义阶段(实例化)发生。且在类的外部无法直接调用,但是在类的内部可以直接调用(在定义时全部变形了)

    封装其实就是变形。

正常的状态:(注意观察下列红色标识)
class A:
    def foo(self):
        print(\'from A foo\')
        self.bar()
    def bar(self):
        print(\'from A bar\')
class B(A):
    def bar(self):
        print(\'from B bar\')
b = B()
print(A.__dict__)  
b.foo()    #正常状态调用 .foo(),因为A是B的父类,B类中没有foo所以向上查找,在A中找到并执行,foo有个self.bar,self指的对象所以可以认为是在执行b.bar() 又搜寻一次父类,在B中找到,所以结果显示from B bar
 
执行结果:
D:\\Python\\Python36-32\\python.exe E:/Python/DAY-20/day20.py
{\'__module__\': \'__main__\', \'foo\': <function A.foo at 0x03873930>, \'bar\': <function A.bar at 0x038738E8>, \'__dict__\': <attribute \'__dict__\' of \'A\' objects>, \'__weakref__\': <attribute \'__weakref__\' of \'A\' objects>, \'__doc__\': None}
from A foo
from B bar

Process finished with exit code 0

隐藏变形:
class A:
    def foo(self):
        print(\'from A foo\')
        self.__bar()     #做了隐藏,这时在执行找到就不是bar了,而是 _A__bar,下面的结果能看出来名字被变形了
    def __bar(self):    #这里等于   _A__bar(self)   所以最后显示  from A bar
        print(\'from A bar\')
class B(A):
    def bar(self):
        print(\'from B bar\')
b = B()
print(A.__dict__)
b.foo()

执行结果:
D:\\Python\\Python36-32\\python.exe E:/Python/DAY-20/day20.py
{\'__module__\': \'__main__\', \'foo\': <function A.foo at 0x03693930>, \'_A__bar\': <function A.__bar at 0x036938E8>, \'__dict__\': <attribute \'__dict__\' of \'A\' objects>, \'__weakref__\': <attribute \'__weakref__\' of \'A\' objects>, \'__doc__\': None}
from A foo
from A bar

Process finished with exit code 0

  

一个封装的例子

class User:
    def __init__(self,name,age,sex):   #这里能看到隐藏了所有的用户信息
        self.__name = name
        self.__age = age
        self.__sex =sex

    def modify_name(self,val):   #定义更改用户名的接口
        if not isinstance(val,str):   #判断是否是字符串
            raise TypeError(\'must be str\')   #如果不是字符串则主动抛出异常
        self.__name = val   #如果是字符串则执行这条,重新赋值

    def dis_info(self):   #显示用户信息
        print(\'\'\'
        Name:%s
        Age:%s
        Sex:%s
        \'\'\'%(self.__name,self.__age,self.__sex))
t = User(\'laochai\',73,\'male\')   #传入参数 用户名为 laochai
t.modify_name(\'abc\')   #更改用户名 abc
t.dis_info()   #打印信息

执行结果:
D:\\Python\\Python36-32\\python.exe E:/Python/DAY-20/day20.py

        Name:abc
        Age:73
        Sex:male
        

Process finished with exit code 0

   Property:内置函数,一个装饰器,功能是自动执行函数,可以在封装中起到去括号的目的,让用户不知道自己被套路了。

class User:
    def __init__(self,name,age,sex):
        self.__name = name
        self.__age = age
        self.__sex =sex

    @property    #加上property装饰name
    def name(self):
        print(self.__name)

    @name.setter  #调用 .setter方法
    def name(self,val):
        self.__name = val   #修改用户名

    @property  #调用property装饰
    def dis_info(self):
        print(\'\'\'
        Name:%s
        Age:%s
        Sex:%s
        \'\'\'%(self.__name,self.__age,self.__sex))
t = User(\'egon\',18,\'male\')
t.name = \'abc\'   #直接修改用户名
t.dis_info   #查看信息时没有加()执行

执行结果:
D:\\Python\\Python36-32\\python.exe E:/Python/DAY-20/day20.py

        Name:abc
        Age:18
        Sex:male
        

Process finished with exit code 0

  

以上是关于面向对象:抽象类,继承的实现原理,封装的主要内容,如果未能解决你的问题,请参考以下文章

day32--面向对象的程序设计之继承实现的原理(继承顺序)封装property

Python--面向对象的程序设计之继承实现的原理(继承顺序)封装property

TypeScript,面向对象,类、构造函数、继承、抽象类、接口和封装

面向对象的程序设计

二 面向对象三大特性

封装继承和多态