python学习笔记-面向对象的继承、多态、封装 组合 继承 派生 接口继承与归一化 继承顺序 子类调用父类父类的方法  多态  封装

定义:

组合指的是,在一个类中以另外一个类的对象作为数据属性,称为类的组合

例子1:

class Hand():
    pass
class Foot:
    pass
class Trunk:
    pass
class Head:
    pass

class Person:
    def __init__(self,id_num,name):
        self.id_num=id_num
        self.name=name
        self.hand=Hand()
        self.foot=Foot()
        self.trunk=Trunk()
        self.head=Head()

p1=Person(11,"steven")
print(p1.__dict__)

例子2:

class School:
    def __init__(self,name,addr):
        self.name=name
        self.addr=addr
    def zhaosheng(self):
        print("%s正在招生"%self.name)
class Course:
    def __init__(self,name,price,period,school):
        self.name=name
        self.price=price
        self.period=period
        self.school=school

s1=School("Python","四川")
s2=School("Python","重庆")

c1=Course("linux",10,"1h",s1) #建立关联

print(c1.school.addr)#结果为:四川

组合的用处:类跟类之间没有共同点,但是有关联就可以用组合实现,它是一种‘有’的关系,比如教授教课,教授有学生等

继承

定义:

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

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

class SubClass1(ParentClass1): #单继承,基类是ParentClass1,派生类是SubClass
    pass
class SubClass2(ParentClass1,ParentClass2): #python支持多继承,用逗号分隔开多个继承的类
    pass

解疑:

有人说继承了父类的所有属性,字类自定义的属性如果跟父类重名了,那就覆盖了父类的。
不存在覆盖一说,父类的属性没有变,只是在子类的属性字典里增加了自己的属性

继承的用处:当类之间有很多相同的功能,提取这些共同的功能做成基类,用继承比较好,比如老师是人,学生是人

派生

定义:
子类也可以添加自己新的属性或者在自己这里重新定义这些属性(不会影响到父类)
注意:一旦重新定义了自己的属性且与父类重名,那么调用新增的属性时,就以自己为准。

在子类中,新建的重名的函数属性,在编辑函数内功能的时候,有可能需要重用父类中重名的那个函数功能,应该是用调用普通函数的方式,即:类名.func()
此时就与调用普通函数无异了,因此即便是self参数也要为其传值

例子2:

class Hero:
    def __init__(self,nickname,aggressivity,life_value):
        self.nickname=nickname
        self.aggressivity=aggressivity
        self.life_value=life_value
    def attack(self,enemy):
        enemy.life_value-=self.aggressivity

class Riven(Hero):
    camp='Noxus'
    def __init__(self,nickname,aggressivity,life_value,skin):
        Hero.__init__(self,nickname,aggressivity,life_value) #调用父类功能
        self.skin=skin #新属性
    def attack(self,enemy): #在自己这里定义新的attack,不再使用父类的attack,且不会影响父类
        Hero.attack(self,enemy) #调用功能
        print('from riven')
    def fly(self): #在自己这里定义新的
        print('%s is flying' %self.nickname)

r1=Riven('jobs',57,200,'')
r1.fly()
print(r1.skin)

接口继承与归一化

继承同时具有两种含义:
含义一,继承基类的方法,并且做出自己的改变或者扩展(代码重用)
含义二,声明某个子类兼容某基类,定义一个接口类,子类继承接口类,并且实现接口中定义的方法

实践中,继承的第一种含义意义并不大,甚至常常是有害的,行为他使得之类与基类出现强耦合;第二种含义非常重要,它又叫接口继承

接口提取了一群类共同的函数,可以把接口当做一个函数的集合。然后让子类去实现接口中的函数。这么做的意义在于归一化,什么叫归一化,就是只要是基于同一个接口实现的类,那么所有的这些类产生的对象在使用时,从用法上来说都一样。

归一化的好处在于:
1.归一化让使用者无需关心对象的类是什么,只需要的知道这些对象都具备某些功能就可以了,这极大地降低了使用者的使用难度。
2.归一化使得高层的外部使用者可以不加区分的处理所有接口兼容的对象集合

例子3:接口继承

import abc #利用abc模块实现抽象类
class All_file(metaclass=abc.ABCMeta):#定义抽象方法,无需实现功能
    @abc.abstractmethod
    def read(self):#子类必须定义读功能
        pass
    @abc.abstractmethod
    def write(self):
        pass

class Disk(All_file):#子类继承抽象类,但是必须定义read和write方法
    def read(self):
        print("disk read")
    def write(self):
        print("disk write")

class Cdrom(All_file):
    def read(self):
        print("Cdrom read")
    pass
class Mem(All_file):
    pass

d1=Disk()
# c1=Cdrom() #报错,子类没有定义抽象方法

继承顺序

1、继承顺序

在Java和C#中子类只能继承一个父类,而Python中子类可以同时继承多个父类,如A(B,C,D)

如果继承关系为非菱形结构,会按照顺序直到找到我们想要的属性

如果继承关系为菱形结构,那么属性的查找方式有两种,分别是:深度优先和广度优先

python学习笔记-面向对象的继承、多态、封装
组合
继承
派生
接口继承与归一化
继承顺序
子类调用父类父类的方法
 多态
 封装

如图的继承关系,在python2中,新式类继承顺序是按广度优先。经典类的继承是按深度优先。

例子:

class A(object):
    def test(self):
        print('from A')
class B(A):
    def test(self):
        print('from B')
class C(A):
    def test(self):
        print('from C')
class D(B):
    def test(self):
        print('from D')
class E(C):
    def test(self):
        print('from E')

class F(D,E):
    # def test(self):
    #     print('from F')
    pass
f1=F()
f1.test()
print(F.__mro__) #只有新式才有这个属性可以查看线性列表,经典类没有这个属性

#新式类继承顺序:F->D->B->E->C->A
#经典类继承顺序:F->D->B->A->E->C
#python3中统一都是新式类
#pyhon2中才分新式类与经典类

2、继承原理

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列表并遵循如下三条准则:
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 run(self):
        print('地铁%s号线欢迎您' % self.line)
        Vehicle.run(self)  #调用父类方法

line13 = Subway('中国地铁', '180m/s', '1000人/箱', '', 13)
line13.run()

方法二:

super()

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):
        # super(Subway,self) 就相当于实例本身 在python3中super()等同于super(Subway,self)
        super().__init__(name, speed, load, power)
        self.line = line

    def run(self):
        print('地铁%s号线欢迎您' % self.line)
        super().run()   #调用父类方法

class Mobike(Vehicle):  # 摩拜单车
    pass

line13 = Subway('中国地铁', '180m/s', '1000人/箱', '', 13)
line13.run()

 多态

多态并不是什么增加的新知识,也就是说python本身就是支持多态性的。这样是增加了程序的灵活性和可扩展性。

多态体现在由同一个类实例化出的多个对象,这些对象执行相同的方法时,执行的过程和结果是不一样的。

#str,list,tuple都是序列类型
s=str('hello')
l=list([1,2,3])
t=tuple((4,5,6))

#我们可以在不考虑三者类型的前提下使用s,l,t
s.__len__()
l.__len__()
t.__len__()

len(s)
len(l)
len(t)

 封装

第一个层面的封装:类就是麻袋,这本身就是一种封装
第二个层面的封装:类中定义私有的,只在类的内部使用,外部无法访问

python 通过遵循一定的数据属性和函数属性的命名约定来达到封装的效果
约定一:任何以单下划线开头的名字都应该是内部的,私有的

class People:
    _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=People("9527","steven","28",5000)
print(p1._star)#结果:earth 说明这是一种约定,python并不会真正限制

约定二:使用双下划线的方式

class People:
    __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=People("9527","steven","28",5000)
#print(p1.__star)#报错,不能访问
print(p1._People__star)#结果为:earth. 因为,以__开头的属性,Python会进行重命名处理。
print(People.__dict__)

第三个层面的封装:

明确区分内外,内部能用外部不能用,内部的实现逻辑,外部无法知晓,并且为封装到内部的逻辑提供一个访问接口给外部使用。

class People:
    __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):
        return self.__star

p1=People("9527","steven","28",5000)
print(p1.get_star())