Python学习笔记-面向对象进阶封装多态继承

Posted wangzhiwei10

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python学习笔记-面向对象进阶封装多态继承相关的知识,希望对你有一定的参考价值。

一、初识继承

1、什么是继承?

  继承是一种创建新类的方式,新建的类可以继承一个或多个父类(python支持多继承),父类又可称为基类或超类,新建的类称为派生类或子类。子类会“遗传”父类的属性,从而解决代码重用问题。

# python中类的继承分为:单继承和多继承
class ParentClass1: #定义父类
    pass

class ParentClass2: #定义父类
    pass

class SubClass1(ParentClass1): #单继承,基类是ParentClass1,派生类是SubClass
    pass

class SubClass2(ParentClass1,ParentClass2): #python支持多继承,用逗号分隔开多个继承的类
    pass

2、继承的使用

技术分享图片
class Dad:
    这个是父类
    money=10
    def __init__(self,name):
        print(爸爸)
        self.name=name
    def hit_son(self):
        print(%s 正在打儿子 %self.name)

class Son(Dad):
    pass

print(Son.__bases__) # 查看所有继承的父类
s1=Son(lionel)
print(s1.name)
print(s1.money)
s1.hit_son()

"""
(<class ‘__main__.Dad‘>,)
爸爸
lionel
10
lionel 正在打儿子
"""
继承的使用

3、总结

  类的继承有两层意义:

  • 改变
  • 扩展

二、接口继承与归一化设计

1、什么时候用继承?

  • 当类之间有显著不同,并且较小的类是较大的类所需要的组件时,用组合比较好
  • 当类之间有很多相同的功能,提取这些共同的功能做成基类,用继承比较好

2、继承同时具有两种含义

  • 继承基类的方法,并且做出自己的改变或者扩展(解决代码重用问题)。
  • 声明某个子类兼容于某基类,定义一个接口类,子类继承接口类,并且实现接口中定义的方法

3、接口继承的使用

技术分享图片
import abc
class All_file(metaclass=abc.ABCMeta): # 接口类,接口类的方法不需要实现,也不需要实例化
    @abc.abstractclassmethod # 子类必须有read功能
    def read(self):
        pass
    @abc.abstractclassmethod # 子类必须有write功能
    def write(self):
        pass

class Disk(All_file):
    def read(self):
        print(disk read)
    def write(self):
        print(disk write)

class Cdrom(All_file):
    def read(self):
        print(cdrom read)
    def write(self):
        print(cdrom write)

class Mem(All_file):
    def read(self):
        print(mem read)
    def write(self):
        print(mem write)

m1=Mem()
m1.read()
m1.write()

"""
mem read
mem write
"""
接口继承的使用

4、总结

  (1)实践中继承的第一种含义意义并不是很大,甚至常常是有害的。因为它使得子类与基类出现强耦合。

  (2)继承的第二种含义非常重要,又叫接口继承。接口继承实质上是要求“做出一个良好的抽象,这个抽象规定了一个兼容接口,使得外部调用者无需关心具体细节,可一视同仁的处理实现了特定接口的所有对象”,这在程序设计上叫做归一化

三、继承顺序之mro线性顺序列表

1、继承的顺序

  Python的类如果继承了多个类,那么其寻找方法的方式有两种,分别是深度优先和广度优先。

  • 当类是经典类时,多继承情况下,会按照深度优先方式查找(python2中区分经典类与新式类,python3中都是新式类)
  • 当类是新式类时,多继承情况下,会按照广度优先方式查找(python2中区分经典类与新式类,python3中都是新式类)
技术分享图片
class A:
    def test(self):
        print(A)

class B(A):
    def test(self):
        print(B)

class C(A):
    def test(self):
        print(C)

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

class E(C):
    def test(self):
        print(E)

class F(D,E):
    # def test(self):
    #     print(‘F‘)
    pass

f1=F()
f1.test() # F-->D-->B-->E-->C-->A
print(F.__mro__) # 查看继承顺序,只有新式才有这个属性可以查看线性列表,经典类没有这个属性
# 新式类继承顺序:F->D->B->E->C->A
# 经典类继承顺序:F->D->B->A->E->C

"""
D
(<class ‘__main__.F‘>, <class ‘__main__.D‘>, <class ‘__main__.B‘>, <class ‘__main__.E‘>, <class ‘__main__.C‘>, <class ‘__main__.A‘>, <class ‘object‘>)
"""
View Code

2、Python中如何实现的继承

  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. 如果对下一个类存在两个合法的选择,选择第一个父类

 四、子类中调用父类方法

技术分享图片
class Vehicle:
    Country=China
    def __init__(self,name,speed,load,power):
        self.name=name
        self.speed=speed
        self.load=load
        self.power=power

    def run(self):
        print(开动啦。。。)

class Subway(Vehicle):
    def __init__(self,name,speed,load,power,line):
        Vehicle.__init__(self,name,speed,load,power)
        self.line=line

    def show_info(self):
        print(self.name,self.line)

    def run(self):
        Vehicle.run(self)
        print(%s %s号线 开动啦 %(self.name,self.line))

line8=Subway(北京地铁,10km/s,100000,,8)
line8.show_info()
line8.run()

"""
北京地铁 8
开动啦。。。
北京地铁 8号线 开动啦
"""
方法一:指名道姓,即父类名.父类方法()
技术分享图片
class Vehicle:
    Country=China
    def __init__(self,name,speed,load,power):
        self.name=name
        self.speed=speed
        self.load=load
        self.power=power

    def run(self):
        print(开动啦。。。)

class Subway(Vehicle):
    def __init__(self,name,speed,load,power,line):
        # Vehicle.__init__(self,name,speed,load,power)
        super().__init__(name,speed,load,power)
        self.line=line

    def show_info(self):
        print(self.name,self.line)

    def run(self):
        # Vehicle.run(self)
        super().run()
        print(%s %s线 开动啦 %(self.name,self.line))

line13=Subway(北京地铁,10km/s,100000,,13)
line13.show_info()
line13.run()

"""
北京地铁 13
开动啦。。。
北京地铁 13线 开动啦
"""
方法二:super()在子类中调用父类方法

五、多态

1、什么是多态?

  由不同的类实例化得到的对象,调用同一个方法,执行的逻辑不同。多态指的是一类事物有多种形态,多态是一种反映在执行时候的一种状态。

 2、多态的使用

技术分享图片
class H2O:
    def __init__(self,name,temp):
        self.name=name
        self.temp=temp

    def turn_ice(self):
        if self.temp < 0:
            print(【%s】温度太低结冰了 %self.name)
        elif self.temp > 0 and self.temp < 100:
            print(【%s】液化为水 %self.name)
        elif self.temp > 100:
            print(【%s】温度太高变成水蒸气 %self.name)

class Water(H2O):
    pass

class Ice(H2O):
    pass

class Steam(H2O):
    pass

w1=Water(,25)
i1=Ice(,-25)
s1=Steam(蒸汽,125)
w1.turn_ice()
i1.turn_ice()
s1.turn_ice()

"""
【水】液化为水
【冰】温度太低结冰了
【蒸汽】温度太高变成水蒸气
"""
多态的使用
技术分享图片
class H2O:
    def __init__(self,name,temp):
        self.name=name
        self.temp=temp

    def turn_ice(self):
        if self.temp < 0:
            print(【%s】温度太低结冰了 %self.name)
        elif self.temp > 0 and self.temp < 100:
            print(【%s】液化为水 %self.name)
        elif self.temp > 100:
            print(【%s】温度太高变成水蒸气 %self.name)

class Water(H2O):
    pass

class Ice(H2O):
    pass

class Steam(H2O):
    pass

w1=Water(,25)
i1=Ice(,-25)
s1=Steam(蒸汽,125)

def func(obj):
    obj.turn_ice()
func(w1)
func(i1)
func(s1)

"""
【水】液化为水
【冰】温度太低结冰了
【蒸汽】温度太高变成水蒸气
"""
多态的使用进阶

3、总结

   多态就是类的继承的两层意义的一个具体的实现机制,即调用不同的类实例化的对象下的相同的方法,实现的过程不一样。

 六、封装

1、什么是封装?

  Python不依赖语言特性去封装数据,而是通过遵循一定的数据属性和函数属性的命名约定来达到封的效果

  • 约定一:任何以单下划线开头的名字都应该是内部的,私有的
  • 约定二:双下划线开头的名字
技术分享图片
class People1:
    star=earth
    def __init__(self,id,name,age,salary):
        self.id=id
        self.name=name
        self.age=age
        self.salary=salary

    def get_id(self):
        print(我是私有方法,我找到的id是[%s] %self.id)

p1=People1(123,lonel,18,10000)
p1.get_id()
"""
我是私有方法,我找到的id是[123]
"""
没有下划线开头的名字,可以访问
技术分享图片
# 定义者是以单下划线开头的,使用者就不应该调用它
class People2:
    _star=earth
    def __init__(self,id,name,age,salary):
        self.id=id
        self.name=name
        self.age=age
        self.salary=salary

    def _get_id(self):
        print(我是私有方法,我找到的id是[%s] %self.id)

p1=People2(123,lonel,18,10000)
p1._get_id()
"""
我是私有方法,我找到的id是[123]
"""
单下划线开头的名字,可以访问
技术分享图片
class People3:
    __star=earth
    def __init__(self,id,name,age,salary):
        self.id=id
        self.name=name
        self.age=age
        self.salary=salary

    def __get_id(self):
        print(我是私有方法,我找到的id是[%s] %self.id)

p1=People3(123,lionel,18,10000)
# p1.__get_id() #报错,因为python将双下划线开头的名字重命名了
print(People3.__dict__) # 查看重命名后的名字为_People3__get_id
p1._People3__get_id()

"""
{‘__module__‘: ‘__main__‘, ‘_People3__star‘: ‘earth‘, ‘__init__‘: <function People3.__init__ at 0x101d60488>, ‘_People3__get_id‘: <function People3.__get_id at 0x101d60598>, ‘__dict__‘: <attribute ‘__dict__‘ of ‘People3‘ objects>, ‘__weakref__‘: <attribute ‘__weakref__‘ of ‘People3‘ objects>, ‘__doc__‘: None}
我是私有方法,我找到的id是[123]
"""
双下划线开头的名字,无法访问
技术分享图片
class People4:
    __star=earth
    def __init__(self,id,name,age,salary):
        self.id=id
        self.name=name
        self.age=age
        self.salary=salary

    def __get_id(self):
        print(我是私有方法,我找到的id是[%s] %self.id)

    # 访问函数(接口函数)
    def get_star(self):
        print(self.__star)

p1=People4(123,lionel,18,10000)
p1.get_star()

"""
earth
"""
定义接口函数访问双下划线开头的名字

2、封装的意义

  封装的真谛在于明确地区分内外,封装的属性可以直接在内部使用,而不能被外部直接使用,然而定义属性的目的终归是要用,外部要想用类隐藏的属性,需要我们为其开辟接口,让外部能够间接地用到我们隐藏起来的属性。

封装真正的意义:

(1)封装数据:将数据隐藏起来这不是目的。隐藏起来然后对外提供操作该数据的接口,然后我们可以在接口附加上对该数据操作的限制,以此完成对数据属性操作的严格控制。
(2)封装方法:目的是隔离复杂度

 3、总结

第一个层面的封装:类就是麻袋,这本身就是一种封装。
第二个层面的封装:类中属性定义私有的,只在类的内部使用,外部无法访问。
第三个层面的封装:明确区分内外,内部的实现逻辑,外部无法知晓,并且为封装到内部的逻辑提供一个访问接口给外部使用。

 





以上是关于Python学习笔记-面向对象进阶封装多态继承的主要内容,如果未能解决你的问题,请参考以下文章

测开之面向对象进阶篇・《多态》

Python 学习笔记 - 面向对象(基础)

python基础学习笔记

Python学习之旅---多态(类的相关知识,面向对象三大特性:继承-多态-封装)

python学习8_1 面向对象(继承多态封装)以及零散概念(组合,property,绑定方法与非绑定方法,反射,内置函数)

python-面向对象进阶