1.面向对象的三大特征
# 面向对象的三大特征:封装、继承、多态
2.类的继承
1.什么是继承?
继承是新建类的一种方式,新建的类被称为子类或者派生类,被继承的类被称为父类或者基类。
2.为什么要用继承
子类可以使用父类的属性和方法。
类解决了对象与对象之间代码冗余的问题
继承解决了类与类之间代码冗余的问题。
3.继承的分类:
经典类:没有继承object类的子子孙孙类的都属于经典类
新式类:继承了object类的子子孙孙类的都属于新式类。
# 在python2中才有经典类和新式类之分,在python3中都是新式类。
4.查看父类
print(子类名.__bases__)
5.类的继承
# 以学生选课系统为例:
# 定义一个公共的类,把学生类和老师类里面 一样的东西拿出来
class People():
school = \'老男孩\'
def __init__(self, name, age, gender):
self.name = name
self.age = age
self.gender = gender
# 学生类
class Student(People):
def __init__(self, name, age, gender, course=None):
if course is None:
course = []
People.__init__(self, name, age, gender)
self.course = course
def choose_course(self, course):
self.course.append(course)
print(\'%s选课成功%s\' % (self.name, course))
# 调用类产生对象
stu1 = Student(\'qql\', 18, \'female\', \'python\')
print(stu1.name)
# 老师类
class Teacher():
def __init__(self, name, age, gender, level):
People.__init__(self, name, age, gender)
self.level = level
def score(self, stu_obj, score):
stu_obj.score = score
print(\'%s给%s打了%s分\' % (self.name, stu_obj.name, score))
t1 = Teacher(\'jason\', 20, \'male\', 10)
print(t1.name)
t1.score(stu1, 80)


3.单继承下的属性查找
有了继承关系后,对象在查找属性的时候,会先在自己的__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() # 要确认到底self是谁
# 练习 父类如果不想让子类覆盖自己的方法,可以采用隐藏方法设置成自己私有的
class Foo:
def __f1(self): # _Foo__f1() # 隐藏属性对外不对内
print(\'Foo.f1\')
def f2(self):
print(\'Foo.f2\')
self.__f1() # _Foo__f1() 指名道姓的拿
class Bar(Foo):
def __f1(self): # _Bar__f1()
print(\'Bar.f1\')
obj = Bar() # 没有传入参数的生成的对象就是一个空字典 {}
obj.f2() # 要确认self到底是谁


4.多继承下的属性查找
1.经典类(菱形问题)(了解)--深度优先
如果继承多个类,从左往右查找。左边分支到最深后,到左二分支...
# 经典类:深度优先
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
obj = D()
obj.test()
2.新式类(分支不相交)--广度优先
在父类从左右到查找,先不查到最深的那个
查到最右边的父类再到最深那个
# 新式类:广度优先
class A(object):
def test(self):
print(\'from A\')
class B(A):
pass
class C(A):
pass
class D(B):
pass
class E(C):
pass
class F(D, E):
pass
f1 = F()
f1.test()


5.关键字super()和mro()
调用super()会得到一个特殊的对象,该对象专门用来引用父类的属性,并且严格按照C3线性(mro)的顺序向后查找。使用时会自动传入self参数,不能在写,否则参数多了。
Python 2 中: super(自己的类名, self).__init__( 参数1, 参数2 ···) 完整写法。
Python 3 中: super().__init__( 参数1, 参数2 ···) 简写。
class People():
school = \'SH\'
def __init__(self, name, age, gender):
self.name = name
self.age = age
self.gender = gender
class Teacher(People):
def __init__(self, name, age, gender, level):
self.level = level
super().__init__(name, age, gender) # super的使用:按照C3线性查找
print(Teacher.__mro__) # C3线性 : (<class \'__main__.Teacher\'>, <class \'__main__.People\'>, <class \'object\'>)
t1 = Teacher(\'qql\', 20, \'male\', 10)
# 练习
# mro列表练习1
class A():
def test(self):
print(\'from A.test\')
super().test()
class B:
def test(self):
print(\'from B\')
class C(A, B):
pass
c = C()
c.test()
print(C.__mro__) # 查看C3线性
# mro列表练习2
class B:
def test(self):
print(\'B---->test\')
def xixi(self):
print(\'B---->aaa\')
class A:
def test(self):
print(\'A---->test\')
super().xixi()
class C(A, B):
def aaa(self):
print(\'C----->aaa\')
c = C()
c.test()
print(C.mro()) # 查看C的C3线性继承



6.多态与多态性与鸭子类型
1.什么是多态
多态即事物的多种状态
2. # 抽象类只能被继承,不能被实例化
导入abc模块,在父类体代码中方法的顶上紧贴着使用@abc.abstractmethod,会把该方法变成抽象方法,抽象方法体中不能写代码,只能写pass;子类体代码中必须要有一样的方法名来实现,否则就会报错,这种方法在python中不推荐使用。
import abc
class Animal(metaclass=abc.ABCMeta):
@abc.abstractmethod # 紧贴着的下面那个方法变成抽象方法,也就是说子类必须有该方法实现
def speak(self):
pass # 抽象方法体不能写代码,只能写pass
@abc.abstractmethod # 紧贴着的下面那个方法变成抽象方法,也就是说子类必须有该方法实现
def login(self):
pass # 抽象方法体不能写代码,只能写pass
# a1 = Animal() 实例化会报错
class People(Animal):
def speak(self):
print(\'嗷嗷嗷\')
def login(self):
pass
class Pig(Animal):
def speak(self):
print(\'哼哼哼\')
def login(self):
pass
class Dog(Animal):
def speak(self):
print(\'汪汪汪\')
obj = People()
obj.speak()
3. # 多态推荐用法--鸭子类型
定义一个有参函数,传入对象返回值 对象.方法名()
# 例子
# 多态练习
class Pig():
def speak(self):
print(\'哼哼哼\')
class Dog():
def speak(self):
print(\'汪汪汪\')
class Txt():
def speak(self):
print(\'Txt\')
# 定义一个有参函数,传入对象返回对象.方法名()
def animal(animal):
return animal.speak()
# 调用类产生对象
obj1 = Pig()
obj2 = Dog()
obj3 = Txt()
# 多态带来的特性:在不用考虑对象数据类型的情况下,可以直接调用对应的函数
animal(obj1) # obj1.speak()
animal(obj2) # obj2.speak()
animal(obj3) # obj3.speak()
4. # 父类限制子类的行为
父类有该方法,用raise主动报错限制子类必须有该方法,如果子类没有的话,找到父类那里就会主动报错。
class Animal():
def speak(self):
# 主动报错:如果子类里面没有该方法又想用父类的方法,就会报错
raise Exception("必须实现speak方法")
class Pig(Animal):
def speak(self):
print(\'哼哼哼\')
class Dog(Animal):
school = \'老男孩\'
print(school)
class Txt(Animal):
def speak(self):
print(\'Txt\')
obj1 = Dog()
obj1.speak()
5.鸭子类型(duck teying)
不依赖于继承,只需要制造出外观和行为相同对象,同样可以实现不考虑对象类型而使用对象。这正是Python崇尚的“鸭子类型”(duck typing):“如果看起来像、叫声像而且走起路来像鸭子,那么它就是鸭子”。比起继承的方式,鸭子类型在某种程度上实现了程序的松耦合度。
#二者看起来都像文件,因而就可以当文件一样去用,然而它们并没有直接的关系
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


