Python之面向对象的组合、多态、菱形问题、子类中重用父类的两种方式

一、组合

'''
1、什么是组合
    组合就是一个类的对象具备某一个属性,该属性的值是指向另外一个类的对象


2、为何用组合
    组合也是用来解决类与类直接代码冗余问题的

3、如何用组合

'''
# 继承减少代码冗余,但是将类与类进行了强耦合,python不崇尚,所以能不用继承就尽量不用继承
class OldboyPeople:
    school = 'oldboy'

    def __init__(self, name, age, sex):
        self.name = name
        self.age = age
        self.sex = sex

class OldboyStudent(OldboyPeople):
    def __init__(self,name,age,sex,stu_id):
        OldboyPeople.__init__(self,name,age,sex)
        self.stu_id=stu_id

    def choose_course(self):
        print('%s is choosing course' %self.name)

class OldboyTeacher(OldboyPeople):

    def __init__(self, name, age, sex, level):
        OldboyPeople.__init__(self,name,age,sex)         #重用父类中的属性(功能)(减少代码冗余)------普通的函数传参,该传几个参数传几个参数
        self.level=level

    def score(self,stu,num):          #老师有修改学生成绩的功能,所以将学生对象和修改的成绩传入
        stu.score=num
        print('老师[%s]为学生[%s]打分[%s]' %(self.name,stu.name,num))


stu1=OldboyStudent('猪哥',19,'male',1)
tea1=OldboyTeacher('egon',18,'male',10)

stu1.choose_course()          #学生对象选择课程------猪哥 is choosing course
tea1.score(stu1,100)          #对象的绑定方法--------老师[egon]为学生[猪哥]打分[100]
print(stu1.__dict__)          #查看学生的字典属性----{'name': '猪哥', 'age': 19, 'sex': 'male', 'stu_id': 1, 'score': 100}




# #定制课程类
class Course:                 #将学生和老师对象都有课程属性,所以将他们都有的属性抽出来,重新定义了一个课程类
    def __init__(self,name,period,price):           #调用类时自动触发
        self.name=name
        self.period=period
        self.price=price
    # 查看课程信息
    def tell_info(self):
        msg="""
        课程名:%s
        课程周期:%s
        课程价钱:%s
        """ %(self.name,self.period,self.price)
        print(msg)
#定制人类--------老师和学生类都是人类
class OldboyPeople:
    school = 'oldboy'
    # 定制人类独有的数据属性
    def __init__(self, name, age, sex):
        self.name = name
        self.age = age
        self.sex = sex

class OldboyStudent(OldboyPeople):
    def __init__(self,name,age,sex,stu_id):
        OldboyPeople.__init__(self,name,age,sex)     #类直接点类体代码的函数属性,在子类中重用父类功能,就是一个普通的函数
        self.stu_id=stu_id                           #子类派生出自己独有的数据属性

    def choose_course(self):                         #子类派生出自己独有的数据属性
        print('%s is choosing course' %self.name)

class OldboyTeacher(OldboyPeople):

    def __init__(self, name, age, sex, level):
        OldboyPeople.__init__(self,name,age,sex)     #子类重用父类中的方法
        self.level=level                             #子类中派生出自己独有的属性

    def score(self,stu,num):                         #子类派生出自己独有的函数属性
        stu.score=num
        print('老师[%s]为学生[%s]打分[%s]' %(self.name,stu.name,num))

# 创造课程----------------------------------对象
python=Course('python全栈开发','5mons',3000)         #调用课程对象,产生课程对象,并自动触发__init__函数的执行
linux=Course('linux运维','5mons',800)
# python.tell_info()    #课程对象直接绑定课程类下的方法,并将课程对象当做第一个对象自动传入
# linux.tell_info()


# 创造学生与老师-----------------------------对象
stu1=OldboyStudent('猪哥',19,'male',1)
tea1=OldboyTeacher('egon',18,'male',10)

# 重点
# 将学生、老师与课程对象关联/组合-------------将对象之间进行关联
# --------------------------关键-----------------------------------------------
stu1.course=python          #---------------将学生对象stu1与课程对象python关联
tea1.course=linux           #---------------将老师对象stu1与课程对象linux关联
# --------------------------关键-----------------------------------------------
'''组合就是一个类的对象具备某一个属性,该属性的值是指向另外一个类的对象'''
# 即学生类产生的学生对象具备课程的属性,该课程的属性值是指向课程类中的课程对象的


stu1.course.tell_info()     #--------------等价于python.tell_info(),会打印出学生的课程信息
tea1.course.tell_info()     #--------------等价于linux.tell_info(),会打印出老师的课程信息

二、菱形继承问题

#coding:utf-8
'''
1、菱形继承
    当一个子继承多个父类时,多个父类最终继承了同一个类,称之为菱形继承

2、菱形继承的问题:
    python2区分经典类与新式类,如果子的继承是一个菱形继承,那么经典类与形式的区别为?
        经典类下查找属性:深度优先查找-----------------一条道走到黑
        新式类下查找属性:广度优先查找------------------最后才去找菱形的端点
'''

class G(object):
    # def test(self):
    #     print('from G')
    pass

class E(G):
    # def test(self):
    #     print('from E')
    pass

class B(E):
    # def test(self):
    #     print('from B')
    pass

class F(G):
    # def test(self):
    #     print('from F')
    pass

class C(F):
    # def test(self):
    #     print('from C')
    pass

class D(G):
    # def test(self):
    #     print('from D')
    pass

class A(B,C,D):
    def test(self):
        print('from A')
    # pass

obj=A()
#C3算法只试用新式类,经典类不适用
print(A.mro())  #mro是一种C3算法,[<class '__main__.A'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.F'>, <class '__main__.D'>, <class '__main__.G'>, <class 'object'>]
obj.test()       #A->B->E-C-F-D->G-object-------对象属性的查找会严格按照mro列表的顺序进行查找

三、在子类派生的新方法中重用父类功能的另种方式

# 在子派生的新方法中重用父类功能的两种方式
# 方式一:与继承无关
#指名道姓法,直接用:类名.函数名
class OldboyPeople:
    school = 'oldboy'
    def __init__(self, name, age, sex):
        self.name = name
        self.age = age
        self.sex = sex

class OldboyStudent(OldboyPeople):
    def __init__(self,name,age,sex,stu_id):
        OldboyPeople.__init__(self,name,age,sex)      #-------(减少代码冗余)指名道姓法,直接用:类名.函数名,按照函数传参的规则进行传参即可
        self.stu_id=stu_id                            #子类派生出自己独有的数据属性

    def choose_course(self):                          #子类派生出自己的函数属性
        print('%s is choosing course' %self.name)


# 方式二:严格以mro列表继承属性查找关系
# super()会得到一个特殊的对象,该对象就是专门用来访问父类中的属性的(按照继承的关系)
# super().__init__(不用为self传值)------------可以理解为super()是一个特殊的对象,所以对象绑定方法有一个自动传值的效果
# 注意:
# super的完整用法是super(自己的类名,self),在python2中需要写完整,而python3中可以简写为super()
class OldboyPeople:
    school = 'oldboy'

    def __init__(self, name, age, sex):
        self.name = name
        self.age = age
        self.sex = sex

class OldboyStudent(OldboyPeople):
      # 定制学生类的数据属性,在定制数据属性的过程中,重用了父类中的数据属性
    def __init__(self,name,age,sex,stu_id):
        # OldboyPeople.__init__(self,name,age,sex)         #------父类功能重用方法一
        super(OldboyStudent,self).__init__(name,age,sex)   #------父类功能重用方法二
        self.stu_id=stu_id
    #定制学生类的函数属性
    def choose_course(self):
        print('%s is choosing course' %self.name)

stu1=OldboyStudent('猪哥',19,'male',1)
print(stu1.__dict__)  #-------查看学生对象的字典属性,{'name': '猪哥', 'age': 19, 'sex': 'male', 'stu_id': 1}
print(OldboyStudent.mro()) #------查看学生类的继承关系,[<class '__main__.OldboyStudent'>, <class '__main__.OldboyPeople'>, <class 'object'>]



# 继承顺序查找再应用:
class A:
    def f1(self):
        print('A.f1')
class B:
    def f2(self):
        super().f1()         #B类和A类没有继承关系,但是会按照mro列表,从该类的下一个类继续进行查找,即此时会到A中进行查找
        print('B.f2')

class C(B,A):
    pass

obj=C()
print(C.mro()) #C-》B->A->object,查看C类的继承关系,[<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>]
obj.f2()       #对象属性的查找会严格按照mro列表的顺序进行查找,自己的对象没有到自己的类中找,自己的类中没有,到父类中找,此时父类查找的先后顺序就是按照mro列表来的

# 打印结果:
'''
A.f1
B.f2
'''

四、多态与多态性

'''
1 什么是多态
    多态指的是同一种事物的多种形态
        水-》冰、水蒸气、液态水
        动物-》人、狗、猪

2 为和要用多态
    多态性:
    继承同一个类的多个子类中有相同的方法名
    那么子类产生的对象就可以不用考虑具体的类型而直接调用功能

3 如何用
'''
import abc
class Animal(metaclass=abc.ABCMeta): #定义一个抽象类,规范一个类的类
    @abc.abstractmethod              #装饰器装饰后,意味着但凡继承类该类的方法,函数属性的属性名都必须与父类的函数属性名一致,否则就会报错
    def speak(self):
        pass
    @abc.abstractmethod
    def eat(self):
        pass

# Animal() #强调:父类是用来制定标准的,不能被实例化-------此时实例化就会报错,这并不是python崇尚的,python更多的是一种约定俗成的,而不是硬性的限制
class People(Animal):
    def speak(self):
        print('say hello')

    def eat(self):
        pass

class Dog(Animal):
    def speak(self):
        print('汪汪汪')

    def eat(self):
        pass
class Pig(Animal):
    def speak(self):
        print('哼哼哼')

    def eat(self):
        pass

peo1=People()
dog1=Dog()
pig1=Pig()
#
#继承同一个动物类的多个子类(人、狗、猪类)中有相同的方法名(speak\eat),那么子类产生的对象就可以不用考虑具体的类型而直接调用功能
peo1.speak()      #不用考虑具体的对象类型,而直接调用speak的功能
dog1.speak()
pig1.speak()

# -----------------------不用考虑是什么动物来调用speak的函数属性,所以将其封装成一个功能-----------------------------------
def my_speak(animal):
    animal.speak()
my_speak(peo1)
my_speak(dog1)
my_speak(pig1)
# -----------------------不用考虑是什么动物来调用speak的函数属性,所以将其封装成一个功能-----------------------------------



# 不同的数据类型,他们都有统计长度的这个方法,所以我们想到的是将他们制定成一套方法,便于对不同的数据类型进行统计
l=[1,2,3]
s='helllo'
t=(1,2,3)

print(l.__len__())          #用同一种形式去调用的好处就是将其功能封装成一个函数时,只需要传入对象就可以了
print(s.__len__())          #如果用不同的方法名去统计不同数据类型的长度,这样封装成函数时就需要制定不同的函数去封装
print(t.__len__())

# def len(obj):
#     return obj.__len__()

print(len(l)) # l.__len__()
print(len(s)) #s.__len__()
print(len(t))







# python推崇的是鸭子类型,只要你叫的声音像鸭子,并且你走路的样子也像鸭子,那你就是鸭子
class Disk:
    def read(self):
        print('disk read')

    def write(self):
        print('disk wirte')


class Process:
    def read(self):
        print('process read')

    def write(self):
        print('process wirte')


class File:
    def read(self):
        print('file read')

    def write(self):
        print('file wirte')



obj1=Disk()
obj2=Process()
obj3=File()



obj1.read()
obj1.write()

猜你喜欢

转载自www.cnblogs.com/sui776265233/p/9234603.html