python3基础-组合和继承

组合

软件重用的重要方式除了继承之外还有另外一种方式,即:组合

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

class school:
    def __init__(self,name,addr):
        self.name=name
        self.addr=addr

    def type(self):
        print("%s 是 公立学校"%self.name)

class student:
    '学生的信息'
    empcount = 0
    def __init__(self,name,sex,age,school):
        self.name= name
        self.sex = sex
        self.age = age
        self.school =school

    def type(self):
        self.school.type()  # 通过对象去调用type函数
        print('哈哈哈哈哈')

stu1 =student('susu','未知',18,school('北大','北京'))
print(stu1.__dict__)
print(stu1.name)
print(stu1.school.name)
print(stu1.school.addr)
stu1.type()

继承

继承是一种创建新类的方式

在python中,新建的类可以继承一个或多个父类,新建的类可称为子类或派生类,父类又可以称为基类或超类

class Parent1: #定义父类 基类 超类
    pass

class Parent2: #定义父类 基类 超类
    pass

class Subclass1(Parent1):#单继承 子类 派生类
    pass

class Subclass2(Parent1,Parentc2):#多继承 子类 派生类
    pass

#类的内置属性__bases__可以查看类继承的所有父类
print(Subclass2.__bases__)
print(Subclass1.__bases__)

(<class '__main__.Parent1'>, <class '__main__.Parent2'>)
(<class '__main__.Parent1'>,)

继承顺序之MRO线性顺序

经典类:没有显示地继承object类的类,以及该类的子类,都是经典类==》深度查找

新式类:显示地继承object类的类,以及该类的子类,都是新式类==》广度查找

 

 

 

 

 

 

 经典类顺序(深度):F--D--B--A 后F --E--C--A

新式类顺序(广度): F--D--B (不找基类)后 F--E--C--A

 新式类可以直接通过 类名.__mro__的方式取类的MRO,也可以通过类目.mro()的形式

class A:
    pass

class B:
    pass

class C(A,B):
    pass


print(C.__mro__)
print(C.mro())

”“”
执行结果
(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>)
[<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]
“”“

在python3中都是同样的新式类,即使没有显示的继承object,也会默认继承该类

print(Parent1.__bases__)  #输出  (<class 'object'>,)
print(Parent2.__bases__)  #输出   (<class 'object'>,) 

实例

class People:
    '''
    定义一个人类,
    姓名、性别、年龄
    '''
    def __init__(self,name,sex,age):
        self.name=name
        self.sex=sex
        self.age=age
class Student(People):
    '''
    定义一个学生类
    '''
    def studying(self):
        print('%s is studying'%self.name)
class Teacher(People):
    '''
    定义一个老师类
    '''
    def teach(self):
        print('%s is teaching'%self.name)

直接调用Teacher()

teacher1= Teacher() 
#未传入参数,则会报错TypeError: __init__() missing 3 required positional arguments: 'name', 'sex', and 'age'

Teacher类并没有定义__init__方法,但是会从父类中找到__init__则

teacher1=Teacher('苏苏','未知',20)
print(teacher1.school,teacher1.name)
teacher1.teach()
#=============
"""
执行结果如下
福大 苏苏
苏苏 is teaching
"""

总结:因为有了继承关系,对象在查找属性时,先从对象自己的__dict__中找,如果没有则去子类中找,然后再去父类中找
继承-子类和父类的数据属性重名的问题

class People:
    '''
    定义一个人类,
    姓名、性别、年龄
    '''
    school = '福大'
    def __init__(self,name,sex,age):
        self.name=name
        self.sex=sex
        self.age=age
class Student(People):
    '''
    定义一个学生类
    '''
    def studying(self):
        print('%s is studying'%self.name)
class Teacher(People):
    '''
    定义一个老师类
    '''
    school = '北大'  #子类自定义的属性和父类People的属性重名
    def teach(self):
        print('%s is teaching'%self.name)
teacher1 = Teacher('susu','未知',50)
print(teacher1.school) #输出子类的 school为北大
print(People.school)  #输出父类的 school 为福大  ;并不会被子类覆盖

派生与方法重用

父类中没有的属性  子类中出现 叫派生属性
父类中没有的方法  子类中出现 叫派生方法
父类和子类都有 调用子类  想调用父类的药指出父类名称调用

每个老师还有职称这个属性,则就需要在Teacher类中定义该类自己的__init__

class People:
    school = '福大'
    def __init__(self,name,sex,age):
        self.name=name
        self.sex=sex
        self.age=age

class Student(People):
    def studying(self):
        print('%s is studying'%self.name)


class Teacher(People):
    def __init__(self,name,sex,age,titles):
        self.name = name  #重复代码
        self.sex =sex    #重复代码
        self.age =age    #重复代码
        self.titles = titles

    def teach(self):
        print('%s is teaching'%self.name)


teacher1 = Teacher('susu','',29,'高级讲师') #只会找Teacher类中的__init__,并不会自动调用父类的
print(teacher1.name,teacher1.sex,teacher1.age,teacher1.titles)
#输出的是susu 女 29 高级讲师

若想在子类派生的方法内重用父类的功能

1、直接调用某一个类的函数

class Teacher(People):
    def __init__(self, name, sex, age, titles):
        People.__init__(self,name,sex,age) #调用的是函数,因而需要传入self
        self.titles = titles

    def teach(self):
        print('%s is teaching'%self.name)

2、super()

调用super()会得到一个特殊的对象,该对象专门用来引用父类的属性,且严格按照MRO规定的顺序向后查找

class Teacher(People):
    def __init__(self, name, sex, age, titles):
        super().__init__(name,sex,age) #调用的是绑定方法,自动传入self
        self.titles = titles

    def teach(self):
        print('%s is teaching'%self.name)

组合和继承区别

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

继承的作用

(1)减少重复代码,子类可扩展或者可以修改【但是往往在实践中,该含义意义并不很大,甚至常常是有害的,因为它使得子类与基类出现强耦合。】
(2)声明某个子类兼容于某基类,定义一个接口类,子类继承接口类,并且在子类实现接口中定义的方法————————————接口继承
【目的:规范子类,子类实现接口类的中定义的方法,并且不必要实例化】
【接口继承实质上是要求“做出一个良好的抽象,这个抽象规定了一个兼容接口,使得外部调用者无需关心具体细节,一视同仁的处理实现了特定接口的所有对象”一这在程序设计上, 叫做归一化。】

抽象类

抽象类的本质上也是类,但是抽象类只能够被继承,不能进行实例化,也就是说可以当父类,但是不能生成对象。

当子类继承抽象类的时候,如果抽象类定义了抽象方法,那么子类必须要定义同名的方法,即父类限制:

1、子类必须要有父类的方法
2、子类实现的方法必须跟父类的方法的名字一样
import  abc
class Animal(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def eat(self):
        pass
    @abc.abstractmethod
    def action(self):
        pass

class Cat(Animal):

    def eat(self,name):
        print('%s 在吃鱼' %name)

cat1 =Cat()  
#会报错TypeError: Can't instantiate abstract class Cat with abstract methods action
#因为子类Cat 没有和父类的方法一样,缺失一个action方法
import  abc
class Animal(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def eat(self):
        pass
    @abc.abstractmethod
    def action(self):
        pass

class Cat(Animal):

    def eat(self,name):
        print('%s 在吃鱼' %name)

    def action(self,name):
        print('%s 在喵喵叫' %name)

#ani = Animal()
#不能进行实例化,会报错TypeError: Can't instantiate abstract class Animal with abstract methods action, eat

cat1 =Cat()
cat1.eat('小黑猫')
cat1.action('小黑猫')
”“”
小黑猫 在吃鱼
小黑猫 在喵喵叫
“”“

 

 

 

 

 

posted @ 2019-11-05 17:19  槑槑DE  阅读(250)  评论(0编辑  收藏  举报