继承
1.什么是继承
I:继承就是一种创建新类的方式,新建的类可以称为子类或者派生类,父类可以称为基类或超类,子类会遗传父类的属性
II:需要注意的是:python支持多继承
在python中,新建的类可以继承一个或多个父类
class Parent1(object):
x=1111
class Parent2(object):
pass
class Sub1(Parent1): # 单继承
pass
class Sub2(Parent1,Parent2): # 多继承
pass
我们可以通过__base__方法来查看该类的父类
print(Sub1.__bases__) # (<class '__main__.Parent1'>,)
print(Sub2.__bases__) # (<class '__main__.Parent1'>, <class '__main__.Parent2'>)
print(Sub1.x) # 1111
ps1:在python2中有经典类与新式类之分
新式类:继承了objecct类的子类,以及孩子类的子类子子类
经典类:没有继承object类的子类,以及孩子类的子类子子类
ps2:在python3中没有继承任何类,那么会默认继承object类,所以python3中所有的类都是新式类
print(Parent1.__bases__) # (<class 'object'>,)
print(Parent2.__bases__) # (<class 'object'>,)
III:python的多继承
优点:子类可以同时遗传多个父类的属性,最大限度地重用代码
缺点:
1.违背人的思维习惯,继承表达的是一种"什么"是"什么"的关系
2.代码可读性会变差
3.不建议使用多继承,有可能会引发可恶的菱形问题,扩展性变差
如果真的涉及到一个子类不可避免地要重用多个父类的属性,应该使用Mixins
2.为什么要用继承:用来解决类与类之间代码冗余问题
3.如何实现继承
示例1:解决类与类之间存在的冗余问题
class Student:
school='OLDBOY'
def __init__(self,name,age,sex):
self.name=name
self.age=age
self.sex=sex
def choose_course(self):
print('学生%s 正在选课' %self.name)
class Teacher:
school='OLDBOY'
def __init__(self,name,age,sex,salary,level):
self.name=name
self.age=age
self.sex=sex
self.salary=salary
self.level=level
def score(self):
print('老师 %s 正在给学生打分' %self.name)
示例2 基于继承解决类与类之间的冗余问题
class OldboyPeople:
school = 'OLDBOY'
def __init__(self, name, age, sex):
self.name = name
self.age = age
self.sex = sex
class Student(OldboyPeople):
def choose_course(self):
print('学生%s 正在选课' % self.name)
class Teacher(OldboyPeople):
# 老师的空对象,'egon',18,'male',3000,10
def __init__(self, name, age, sex, salary, level):
# 指名道姓地跟父类OldboyPeople去要__init__
OldboyPeople.__init__(self,name,age, sex)
self.salary = salary
self.level = level
def score(self):
print('老师 %s 正在给学生打分' % self.name)
tea_obj=Teacher('egon',18,'male',3000,10)
单继承下的属性查找
有了继承关系,对象在查找属性时,先从对象自己的__dict__中找,如果没有则去子类中找,然后再去父类中找
示例一:
class Foo:
def f1(self):
print('Foo.f1')
def f2(self):
print('Foo.f2')
self.f1()
class Bar(Foo):
def f1(self):
print('Bar.f1')
obj=Bar()
obj.f2()
# Foo.f2
# Bar.f1
# b.f2()会在父类Foo中找到f2,先打印Foo.f2,然后执行到self.f1(),即b.f1(),仍会按照:对象本身->类Bar->父类Foo的顺序依次找下去,在类Bar中找到f1,因而打印结果为Foo.f1
示例二:父类如果不想让子类覆盖自己的方法,可以采用双下划线开头的方式将方法设置为私有的
class Foo:
def __f1(self): # _Foo__f1
print('Foo.f1')
def f2(self):
print('Foo.f2')
self.__f1() # self._Foo__f1,# 调用当前类中的f1
class Bar(Foo):
def __f1(self): # _Bar__f1
print('Bar.f1')
obj=Bar()
obj.f2()
多继承带来的菱形问题
一:菱形问题介绍与MRO
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,C):
pass
print(C.mro()) # 类C以及类C的对象访问属性都是参照该类的mro列表
# [<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
obj = D()
obj.test() # 结果为:from B
总结:类相关的属性查找(类名.属性, 该类的对象.对象),都是参照该类的mro
二:如果多继承是非菱形继承,经典类与新式的属性查找顺序一样:
都是一个一个分支的找下去,然后最后找object
class E:
def test(self):
print('from E')
class F:
def test(self):
print('from F')
class B(E):
def test(self):
print('from B')
class C(F):
def test(self):
print('from C')
class D:
def test(self):
print('from D')
class A(B, C, D):
# def test(self):
# print('from A')
pass
print(A.mro())
'''
[<class '__main__.A'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.F'>, <class '__main__.D'>, <class 'object'>]
'''
obj = A()
obj.test() # 结果为:from B
# 可依次注释上述类中的方法test来进行验证
三:如果多继承是菱形继承,经典类与新式类的属性查找顺序不一样:
经典类:深度优先,会在检索第一条分支的时候就直接一条道走到黑,即会检索共同的父类
新式类:广度优先,会在检索最后一条分支的时候检索共同的父类
class G: # 在python2中,未继承object的类及其子类,都是经典类
# def test(self):
# print('from G')
pass
class E(G):
# def test(self):
# print('from E')
pass
class F(G):
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')
class D(G):
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->G->object
# 经典类:A->B->E->G->C->F->D
obj = A()
obj.test()
在子类派生的新方法中如何重用父类的功能
方式一:知名道姓的调用某一个类下的函数,不依赖于继承关系
class OldboyPeople:
def __init__(self,name,age,sex):
self.name=name
self.age=age
self.sex=sex
def f1(self):
print('%s say hello' %self.name)
class Teacher(OldboyPeople):
def __init__(self,name,age,sex,level,salary):
OldboyPeople.__init__(self,name,age,sex)
self.level = level
self.salary=salary
tea_obj=Teacher('egon',18,'male',10,3000)
print(tea_obj.__dict__)
方式二:super()调用父类提供给自己的方法=》严格依赖继承关系
调用super()会得到一个特殊的对象,该对象会参照发起属性查找的那个类的mro,去当前类的父类中找属性
class A:
def text(self):
super().text()
class B:
def text(self):
print('from b')
class C(A,B):
pass
obj=C()
obj.text() # from b
print(C.mro()) # [<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]
多态与鸭子类型
1.什么是多态
同一种事物有多种形态
class Animal:
pass
class People(Animal):
pass
class Dog(Animal):
pass
class Pig(Animal):
pass
2.为何要有多态=》多态会带来什么样的特性,多态性
多态性指的是可以在不考虑对象具体类型的情况下而直接使用对象
多态性的好处在于增强了程序的灵活性和可扩展性
class Animal:
def say(self):
print('动物基本发声频率 ',end='')
class People(Animal):
def say(self):
super().say()
print('嘤嘤嘤')
class Dog(Animal):
def say(self):
super().say()
print('汪汪汪')
class Pig(Animal):
def say(self):
super().say()
print('哼哼哼')
obj1=People()
obj2=Dog()
obj3=Pig()
obj1.say()
obj2.say()
obj3.say()
定义统一的接口,接受传入的动物对象
def animal(animal):
animal.say()
animal(obj1)
animal(obj2)
animal(obj3
python推崇的是鸭子类型:“如果看起来像、叫声像而且走起路来像鸭子,那么它就是鸭子”
鸭子类型在某种程度上实现了程序的松耦合度
# 二者看起来都像文件,因而就可以当文件一样去用,然而它们并没有直接的关系
class Txt: # Txt类有两个与文件类型同名的方法,即read和write
def read(self):
pass
def write(self):
pass
class Disk: # Disk类也有两个与文件类型同名的方法:read和write
def read(self):
pass
def write(self):
pass
# 了解:
import abc
class Animal(metaclass=abc.ABCMeta): # 统一所有子类的标准
@abc.abstractmethod
def say(self):
pass
# obj=Animal() # 不能实例化抽象类自己
class People(Animal):
def say(self):
pass
class Dog(Animal):
def say(self):
pass
class Pig(Animal):
def say(self):
pass