1,父类与子类,单继承与多继承
'''
继承是一种创建新类的方式,新建的类可称为子类或派生类,父类又可称为基类或超类,子类会遗传父类的属性
# II:需要注意的是:python支持多继承
'''
# Parent1 和Parent2 是基类/超类
class Parent1(object):
x=1111
class Parent2(object):
pass
# Sub1和Sub2 是子类或派生类
class Sub1(Parent1): # 单继承
pass
class Sub2(Parent1,Parent2): # 多继承
pass
print(Parent1.__bases__)
print(Parent2.__bases__)
# (,)
print(Sub1)
print(Sub2)
#
# 单继承
print(Sub1)
#
# 多继承
print(Sub2.__bases__)
# (, )
# Sub2没有定义x,依然可以从父类取得
print(Sub2.x)
# 111
# III:python的多继承
# 优点:子类可以同时遗传多个父类的属性,最大限度地重用代码
# 缺点:
# 1、违背人的思维习惯:继承表达的是一种什么"是"什么的关系
# 2、代码可读性会变差
# 3、不建议使用多继承,有可能会引发可恶的菱形问题,扩展性变差,
# 如果真的涉及到一个子类不可避免地要重用多个父类的属性,应该使用Mixins
2、为何要用继承:用来解决类与类之间代码冗余问题
3,如何实现继承
示例1,先定义People这个类,学生和老师都属于这个类,People的属性可以给Student和Teacher共享
class People:
school='beijing_university'
def __init__(self,name,age,sex):
self.name=name
self.age=age
self.sex=sex
class Student(People):
def choose(self):
print('%s is choosing a course' %self.name)
class Teacher(People):
def teach(self):
print('%s is teahing' %self.name)
stu1=Student('mz',12,'female')
print(stu1.school,stu1.name,stu1.age,stu1.sex)
stu1.choose()
# mz is choosing a course
t1=Teacher('eg',22,'male')
t1.teach()
# eg is teahing
# 示例2,在父类的基础上,增加初始值
class People:
school='beijing_university'
def __init__(self,name,age,sex):
self.name=name
self.age=age
self.sex=sex
class Teacher(People):
def __init__(self,name,age,sex,subject,salary):
People.__init__(self,name,age,sex)
self.subject=subject
self.salary=salary
@property
def teach(self):
print('%s is teahing' %self.name)
t1=Teacher('mz',12,'female','math',70000)
print(t1.__dict__)
# {'name': 'mz', 'age': 12, 'sex': 'female', 'subject': 'math', 'salary': 70000}
print(t1.name,t1.salary)
# mz 70000
t1.teach
# mz is teahing
4,属性查找
# 示例1
class Foo:
def f1(self):
print('from Foo.f1')
def f2(self):
print('from Foo.f2')
self.f1()
class Bar(Foo):
def f1(self):
print('from Bar.f1')
# 先找到Bar是否有f2,没有再去父类去找f2,打印出from Foo.f2
# 然后执行f2的self.f1(),同样先在自己的类里去找,没有再去父类去找,这里自己的类就有f1
obj=Bar()
obj.f2()
# from Foo.f2
# from Bar.f1
# 示例2: 执行obj.f2() 就想要得出Foo的值
# 可以隐藏f1,通过f2的接口调出Foo的f1
class Foo:
def __f1(self):
print('from Foo.f1')
def f2(self):
print('from Foo.f2')
self.__f1()
class Bar(Foo):
def f1(self):
print('from Bar.f1')
#obj=Bar()
#obj.f2()
# from Foo.f2
# from Foo.f1
a=Foo()
Foo._Foo__f1('s') # 不加参数会报错
# from Foo.f1
5,菱形继承
新式类
新式类(python3):广度优先,会在检索最后一条分支的时候检索大脑袋
(即最后找那个父类)
class A:
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,C):
def test(self):
print('from D')
pass
# 类D以及类D的对象访问属性都是参照该类的mro列表
print(D.mro())
# [, , , , ]
# 以上情况下
obj=D()
obj.test()
# 注释掉D类的test
class D(B,C):
# def test(self):
# print('from D')
pass
obj=D()
obj.test()
# from B
# # 总结:类相关的属性查找(类名.属性,该类的对象.属性),都是参照该类的mro
经典类
# 一条路走到黑,即一条分支走完
class A:
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,C):
# def test(self):
# print('from D')
pass
print(D.mro()) D->B->A->C
6,非菱形继承
如果多继承是非菱形继承,经典类与新式的属性查找顺序一样:
# # 都是一个分支一个分支地找下去,然后最后找object
class E:
# def test(self):
# print('from E')
pass
class F:
def test(self):
print('from F')
class B(E):
# def test(self):
# print('from B')
pass
class C(F):
# def test(self):
# print('from C')
pass
class D:
def test(self):
print('from D')
class A(B, C, D):
# def test(self):
# print('from A')
pass
# 新式类
print(A.mro()) # A->B->E->C->F->D->object
#[, , , , , , ]
obj = A()
obj.test() # 结果为:from F
7,Mixins机制(混合功能)
# 多继承的正确打开方式:mixins机制
# mixins机制核心:就是在多继承背景下尽可能地提升多继承的可读性
# ps:让多继承满足人的思维习惯=》什么"是"什么
class Animials:
pass
class ShitMixin:
def dog(self):
print('like to eat shit')
class cats(Animials):
pass
class dogs(ShitMixin,Animials):
pass
#PS:Mixin为结尾的类,把它看作为一个单独的功能,提升继承的可读性
#如上,在dogs类里继承的ShitMixin,Animials,就可以很直观的知道继承的是Animials类,增加了ShitMixin类这个功能
# 补充:通常Mixin结果的类放在左边
8,派生
# 例1
class Teacher:
def __init__(self,name,sex,age):
self.name=name
self.sex=sex
self.age=age
def f1(self):
print('my name is %s' %self.name)
class Student(Teacher):
def __init__(self,name,sex,age,height,hobby):
Teacher.__init__(self,name,sex,age)
self.height=height
self.hobby=hobby
def f1(self):
print('my name is %s,my hobby is %s' % self.name,self.hobby)
obj1=Teacher('egg','male',33)
print(obj1.__dict__)
# {'name': 'egg', 'sex': 'male', 'age': 33}
obj1.f1()
# my name is egg
obj2=Student('mmz','female',21,170,'sing')
print(obj2.__dict__)
# {'name': 'mmz', 'sex': 'female', 'age': 21, 'height': 170, 'hobby': 'sing'}
# 例2
class Teacher:
def __init__(self,name,sex,age):
self.name=name
self.sex=sex
self.age=age
def f1(self):
print('my name is %s' %self.name)
class Student(Teacher):
def __init__(self,name,sex,age,height,hobby):
super().__init__(name,sex,age)
self.height=height
self.hobby=hobby
def f1(self):
print('my name is %s,my hobby is %s' % (self.name,self.hobby))
# super会依据mro的结果,调用发起者后面的类,即Student发起调用,去找父类Teacher,再去找object
print(Student.mro())
# [, , ]
obj2=Student('mmz','female',21,170,'sing')
obj2.f1()
# my name is mmz,my hobby is sing
# 例3
class A:
def test(self):
print('from A')
super().test()
class B:
def test1(self):
print('from B')
class C(A,B):
def test1(self):
print('from C')
print(C.mro())
# [, , , ]
obj=C()
obj.test() # 会报错
# 依据mro,C发起调用,依次先找A,执行第一行打印'from A',
# 执行第二行super().test(),第二行的test再去找B,B没有test,便报错
# 如果把B的test1改为test,输出的结果为以下
# from A
# from B
#由此可得出,即使肉眼看到的B不是A的父类,在执行的时候也会把他当作父类
9,组合示例
class Course:
def __init__(self,name,price):
self.name=name
self.price=price
def tell_info(self):
print('%s %s' % (self.name,self.price))
class Date:
def __init__(self,year,mon,day):
self.year=year
self.mon=mon
self.day=day
def tell_info(self):
print('%s %s %s' % (self.year,self.mon,self.day))
class People:
school='清华'
def __init__(self,name,sex,age):
self.name=name
self.sex=sex
self.age=age
class Teacher(People):
def __init__(self,name,sex,age,title,year,mon,day):
super().__init__(name,sex,age)
self.birth=Date(year,mon,day)
self.course=[]
def teach(self):
print('%s is teach %s' %(self.name,self.course))
math=Course('math','2w')
teacher1=Teacher('mzz','female',23,'camper',2000,6,2)
teacher1.course.append(math)
# 用Date类得出teacher1的出生年月,注意不能直接teacher1.birth()会报错
teacher1.birth.tell_info()
#course定义为列表形式,所以要有循环得出
for obj in teacher1.course:
obj.tell_info()
# math 2w