首页 > 技术文章 > 面向对象的特征之继承

jgx0 2021-12-06 19:56 原文

今日内容概要

  • 面向对象的三大特征
    • 封装
    • 继承(重要)
    • 多态
  • 继承的属性查找顺序
    • 单继承下的属性查找
    • 多继承下的属性查找
  • super()和mro()列表
  • 多态与多态性(了解)

image

内容详细

封装

# 将功能封装成函数

# 在类中 我们正常定义的方法 就是封装

image

继承(概念)

1.什么是继承
	'''
	继承就是新建类的一种方式 新建的类我们称之为 子类 或者 派生类
	被继承的类称之为 父类 或者 基类
		子类可以使用父类中的属性或方法
	'''
    
2.为什么要用继承
	'''
	类解决了对象与对象之间的代码冗余问题
	继承解决的是类与类之间的代码冗余问题
    '''
3.如何使用继承
	# 新式类
		继承了object类的子子孙孙类
	# 经典类
		没有继承object类的子子孙孙类
	'''
	新式类和经典类只在python2中区分
	python3只有新式类
	'''

image

继承实例

# 以学生选课系统为例

# 定义父类
class People:
    school = 'SH'
    def __init__(self, name, age, gender):
        self.name = name
        self.age = age
        self.gender = gender
        
# 定义学生类(子类)
class Student(People):  # 继承父类People

    def __init__(self, name, age, gender, course=None):
        if course is None:
            course = []
        People.__init__(self, name, age, gender)
        self.courses = course

    def choose_course(self, course):
        self.courses.append(course)
        print('%s 选课成功 %s' % (self.name, self.courses))

stu = Student('ly', 19, 'male')
print(stu.name)  # ly

# 定义老师类(子类)
class Teacher(People):  # 继承父类People

    def __init__(self, name, age, gender, level):
        self.level = level
        People.__init__(self, name, age, gender)

    def score(self, stu_obj, score):
        stu_obj.score = score  # 给学生打分
        print('%s给%s打了%s分' % (self.name, stu_obj.name, score))

tea = Teacher('ly', 19, 'male', 10)
print(tea.name)  # ly
print(tea.level)  # 10

image

单继承下的属性查找

class Foo:
    def f1(self):
        print('Foo.f1')

    def f2(self):
        print('Foo.f2')  # 1
        self.f1()  # self = obj = Bar 所以要找 Bar中的 f1

class Bar(Foo):
    def f1(self):
        print('Bar.f1')  # 2

obj = Bar()  # {}
print(Bar.mro())  # [<class '__main__.Bar'>, <class '__main__.Foo'>, <class 'object'>] 查找空间顺序
obj.f2()
'''
打印结果:
    Foo.f2
    Bar.f1
'''


# 练习
class Foo:
    def __f1(self):  # _Foo__f1()
        print('Foo.f1')

    def f2(self):
        print('Foo.f2')  # 1
        self.__f1()  # _Foo__f1() 此处的__f1是属于Foo类空间产生的f2对象空间中 所以结果是 Foo.f1

class Bar(Foo):
    def __f1(self):  # # _Bar__f1()
        print('Bar.f1')

obj = Bar()  # {}
print(Bar.mro())  # [<class '__main__.Bar'>, <class '__main__.Foo'>, <class 'object'>] 查找空间顺序
obj.f2()
'''
打印结果:
    Foo.f2
    Foo.f1
'''

image

多继承下的属性查找

# 新式类:按照广度优先查询(经典类:按照深度优先查询)
class A(object):
    def test(self):
        print('from A')

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

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

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

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

class F(D, E):
    # def test(self):
    #     print('from F')
    pass

f1 = F()
f1.test()
print(F.mro())  # 查找顺序按照 [<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>] 找不到就下一个类 不能返回查找
'''
打印结果:
	from B
'''

image

super()和mro列表

"""
super()会得到一个对象 从发起属性查找的类的mro列表中按顺序查找
"""
# 父类
class People():
    school = 'SH'

    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)  # 指名道姓的调用父类的方法
        # super(Student, self) 返回了一个特殊的对象
        # 它的使用遵从mro列表
        # super(Student, self).__init__(name, age, gender)  # python2的写法
        super().__init__(name, age, gender)  # python3 的写法
        self.courses = course
        
    def choose_course(self, course):
        self.courses.append(course)
        print('%s 选课成功 %s' % (self.name, self.courses))

stu = Student('ly', 19, 'male')
print(stu.name)  # ly

# 老师类(子类)
class Teacher(People):
    def __init__(self, name, age, gender, level):
        self.level = level
        People.__init__(self, name, age, gender)

    def score(self, stu_obj, score):
        stu_obj.score = score  # 给学生打分
        print('%s给%s打了%s分' % (self.name, stu_obj.name, score))

tea = Teacher('ly', 19, 'male', 10)
print(tea.name)  # ly
print(tea.level)  # 10


# 例题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__)  # 查找顺序 按照mro列表 由C3线性算法产生 (<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>)
'''
打印结果:
	from A.test
	from B
'''


# 例题2
class B:
    def test(self):
        print('B---->test')

    def aaa(self):
        print('B---->aaa')

class A:
    def test(self):
        print('A---->test')
        super().aaa()

class C(A, B):
    def aaa(self):
        print('C----->aaa')

c = A()
c.test()  # 打印结果: A---->test + 报错
print(A.mro())  # 查找顺序 [<class '__main__.A'>, <class 'object'>] A类找不到就直接报错

image

多态和多态性(了解)

1.什么是多态
	水:
        液态水 固态水 气态水
    动物:
        人 猪 狗 猫...
        
# 抽象类 定义
	抽象类只能被继承 不能被实例化
    
# 固定语法
import abc

class Animal(metaclass=abc.ABCMeta):
    @abc.abstractmethod  # 该方法已经是抽象方法了
    def speak(self): pass

    @abc.abstractmethod  # 子类函数必须含有 login 方法 没有就会直接报错
    def login(self): pass
    
class People(Animal):
    def speak(self):
        print('嗷嗷嗷')
        
    def login(self):  # 子类函数必须含有 login 方法 没有就会直接报错
        pass
    
'''
如果定义抽象类了 那么子类当中必须由抽象类的方法名
即使子类函数体代码为pass也可
否则将直接报错 而不会再到抽象类(父类)查找
eg:
class People(Animal):
	def jiao(self):
    	print('嗷嗷嗷')

obj = People()
obj.speak()
打印结果:
	直接报错
'''

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

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

obj = People()
obj.speak()
'''
打印结果:
    嗷嗷嗷
'''   



# 在python3中 抽象类写法墨守成规为以下
# 省去了abc模块固定语法
class People():
    def speak(self):
        print('嗷嗷嗷')

class Pig():
    def speak(self):
        print('哼哼哼')

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

class Txt():
    def speak(self):
        print('Txt')

obj = People()
obj1 = Pig()
obj2 = Dog()
obj3 = Txt()

# 多态带来的特性:在不用考虑对象数据类型的情况下,直接调用对应的函数 封装成函数调用
def animal(animal):
    return animal.speak()

animal(obj)
animal(obj1)
animal(obj2)
animal(obj3)



# len 就是多态一种场景
len('abc')
len([1, 2, 3])
len({'username': 'ly'})

def len(item):
    return item.__len__()
print(len('abc'))




# 父类限制子类的行为(抽象类) 还可以用异常捕获主动报错
class Animal():
    def speak(self):
        raise Exception("必须实现speak方法")  # 主动报错出去
 
class People(Animal):
    pass
 
class Pig():
    def speak(self):
        print('哼哼哼')
 
class Dog():
    def speak(self):
        print('汪汪汪')
        
obj = People()
obj.speak()



# 一切皆文件 只要类中含有read,write方法 就看做文件类
# 这就是python推崇的鸭子类型 不推荐用 抽象类 来限制子类
class Txt:
    def read(self):
        pass

    def write(self):
        pass

class Process():
    def read(self):
        pass

    def write(self):
        pass

image

推荐阅读