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‘>)
"""

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、总结

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

原文地址:https://www.cnblogs.com/wangzhiwei10/p/8976227.html

时间: 2024-07-30 10:56:08

Python学习笔记-面向对象进阶(一)封装、多态、继承的相关文章

Python学习笔记-面向对象进阶(二)

一.反射 1.什么是反射 反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问.检测和修改它本身状态或行为的一种能力(自省). 2.Python面向对象中的反射 通过字符串的形式操作对象相关的属性.python中的一切事物都是对象(都可以使用反射) 3.四个可以实现自省的函数 (1)hasattr(object,name),判断object中有没有一个name字符串对应的方法或属性,检测是否含有某属性. class BlackMedium: feture='Ugly' def _

Python 学习笔记 - 面向对象(类成员)

上一篇学习了Python面向对象的3大特性,封装,继承和多态,这一篇继续学习类成员,包括字段,方法,属性以及他们的修饰符. 1.字段 字段分为静态字段和普通字段.静态字段属于类,而普通字段属于对象,因此静态字段在内存中只保存一份,而普通字段在每个对象中都保存了一份.定义的时候静态字段定义在类的范围里面,而普通字段定义在方法里面. 例如: >>> class Foo:     # 字段(静态字段)     CC = 123     def __init__(self):         #

Python学习之面向对象进阶

面向对象进阶当然是要谈谈面向对象的三大特性:封装.继承.多态 @property装饰器 python虽然不建议把属性和方法都设为私有的,但是完全暴露给外界也不好,这样,我们给属性赋值的有效性九无法保证,因此,为了使得对属性的访问既安全又方便,可以通过属性的getter(访问器)和setter(修改器)方法进行对应的操作,在python中,可以考虑使用@property包装器来包装getter和setter方法 class Person(object): def __init__(self,nam

C++ Primer学习笔记(13)——封装、继承、多态

C++ 是一种典型的面向对象的编程语言,其最显著地特点是封装.继承和多态.充分理解封装.继承.多态是如何实现的,学好C++就不是难事了. 1.封装 封装是将多个细节元素组合在一起,隐藏起来,成为一个类.外部无法了解类内部是如何实现的,只需要考虑它的接口. 例如:将公司员工的姓名.年龄.工号等信息放在类的私有部分. 封装的好处: 避免类内部出现无意的.可能破坏对象状态的用户级错误. 随时间推移可以根据需求改变或缺陷报告来完善类实现,而无需改变用户级代码. 2.继承 该部分转自ruyue_ruyue

Python学习笔记-面向对象

一.什么是面向对象的程序设计 1.面向过程的程序设计 面向过程:核心是过程二字,过程即解决问题的步骤,就是先干什么,再干什么.基于该思想写程序就好比在设计一条流水线,是一种机械式的思维方式. 优点:复杂的过程流程化,进而简单化 缺点:扩展性差 2.面向对象的程序设计 面向对象:核心是对象二字,对象是特征与技能的结合体.基于该思想编写程序就好比在创造一个世界,世界是由一个个对象组成,是一种"上帝式"的思维方式 优点:可扩展性强 缺点:编程复杂度高,容易出现过度设计问题 二.类与对象 对象

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

之前学习的编程方式都是通过面向过程来实现的,对于一些重用的代码,进一步的使用了函数,增强了代码的可读性和重用性.Python同时还支持面向对象的编程. 面向对象有三大特性: 封装 继承 多态 首先来看看封装.封装包括两点,把内容封装到某个地方:调用封装的内容 例1: class c1:     def __init__(self,name,obj):         self.name = name         self.obj = obj class c2:     def __init_

python学习总结(面向对象进阶)

-------------------类属性和实例属性关系------------------- 1.类属性和实例属性关系 1.实例属性 实例对象独有的属性 2.类属性 类名访问类属性 3.实例中无同名属性时,可访问到类属性,当定义同名实例属性时,则无法访问 4.常用的查找指令 1.vars :查看实例内属性 2.dir :显示类属性和所有实例属性 3.type :显示类型 -------------------方法------------------- 1.实例方法 隐含的参数为类实例self

python学习笔记--面向对象的编程和类

一.面向对象的编程 面向对象程序设计--Object Oriented Programming,简称oop,是一种程序设计思想.二.面向对象的特性类:class类,对比现实世界来说就是一个种类,一个模型.一个类即是对一类拥有相同属性的对象的抽象.蓝图.原型.在类中定义了这些对象的都具备的属性(variables(data)).共同的方法. 对象:object对象,也就是指模型造出来的具体的东西.一个对象即是一个类的实例化后实例,一个类必须经过实例化后方可在程序中调用,一个类可以实例化多个对象,每

Python学习笔记——面向对象编程

接下来学习面向对象编程,基础的就不记录了,只记录一些Python特有的或者高级的特性. http://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/0014318645694388f1f10473d7f416e9291616be8367ab5000 1. 类的定义 定义类使用class关键字,后面紧跟类名(首字母大写),接着是从哪个类继承下来的(所有类最终会继承object). 通过类名加参