欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

面向对象三大特性之继承

程序员文章站 2022-05-18 20:51:22
面向对象三大特性之继承 一、继承初体验 什么是继承 继承的作用 如何实现继承法? 小例子 二、如何寻找继承关系 如何寻找继承关系 传统写法,代码冗余 用父类去书写,解决代码冗余 三、在继承属性下对象属性的查找属性 注意 查找顺序演示 四、派生 派生 五、子类继承父类并重用父类的属性的方法 代码 六、 ......

面向对象三大特性之继承

一、继承初体验

  • 什么是继承
    继承是一种新建类的方式,新建的类称之为子类或派生类,继承的父类称之为基类或超类。

    - 在python中,一个子类可以继承多个父类。(面试可能会问)
    - 在其它语言中,一个子类只能继承一个父类。
  • 继承的作用
减少代码冗余
  • 如何实现继承法?
   1) 先确认谁是子类,谁是父类。
   2) 在定义类子类时, 子类名(父类名)。
  • 小例子
#父类
class father1:
    x = 1
    pass

class father2:
    pass

class father3:
    pass

#子类
class sub(father1,father2,father3):
    pass

#子类.__bases__查看父类
print(sub.__bases__)
#(<class '__main__.father1'>, <class '__main__.father2'>, <class '__main__.father3'>)
print(sub.x)
#1

二、如何寻找继承关系

  • 如何寻找继承关系
    - 确认谁是子类
        - 小小明对象 ---> 人子类 ---> 动物父类
        - 猪坚强对象 ---> 猪子类 ---> 动物父类
        - 哈士奇对象 ---> 狗子类 ---> 动物父类

        - 人、猪、狗 都是子类

    - 确认谁是父类
        - 动物类是父类

        - 得先抽象,再继承
            - 抽取对象之间相似的部分,总结出类
            - 抽取类之间相似的部分,总结出父类。
  • 传统写法,代码冗余
#老师类
class teacher:
    school = 'oldboy'
    country = 'china'
    def __init__(self, name, age, sex):
        self.name = name
        self.age = age
        self.sex = sex

    #老师改分
    def change_limit(self):
        print(f'老师{self.name}开始批改分数...')


#学生类
class student:
    school = 'oldboy'
    country = 'china'

    def __init__(self, name, age, sex):
        self.name = name
        self.age = age
        self.sex = sex

    #学生选课
    def choose_course(self, name, age, sex):
        self.name = name
        self.age = age
        self.sex = sex

tea1 = teacher('tank', 28, 'male')
print(tea1.name, tea1.age, tea1.sex)  #tank 28 male

stu1 = student('yafeng', 18, 'male')
print(stu1.name, stu1.age, stu1.sex)  #yafeng 18 male

'''这样写虽然也可以拿到我想要的信息,但是代码很长,且有很多代码重复,下面的父类就是解决这种情况'''
  • 用父类去书写,解决代码冗余
#父类
class oldpeople:
    school = 'oldboy'
    country = 'china'

    def __init__(self, name, age, sex):
        self.name = name
        self.age = age
        self.sex = sex

#子类
#学生类
class oldstudent(oldpeople):

    def chose_course(self):
        print(f'学生{self.name}正在选课!')


#老师类
class oldteacher(oldpeople):

    def change_score(self):
        print(f'老师{self.name}正在修改分数')

obj1 = oldstudent('yafeng', 18, 'male')
print(obj1.name, obj1.age, obj1.sex)  #yafeng 18 male


obj2 = oldteacher('tank', 25, 'male')
print(obj2.name, obj2.age, obj2.sex)  #tank 25 male

三、在继承属性下对象属性的查找属性

  • 注意
'''
注意: 程序的执行顺序是由上到下,父类必须定义在子类的上方。
- 在继承背景下,对象属性的查找顺序:
    1.先从对象自己的名称空间中查找
    2.对象中没有,从子类的名称空间中查找。
    3.子类中没有, 从父类的名称空间中查找,若父类没有,则会报错!

'''
  • 查找顺序演示
#父类
class father:
    x = 10
    pass


#子类
class sub(father):
    x = 100
    pass
obj1 = sub()
# print(obj1.x)  #100  先从对象自己的名称空间中查找
# print(obj1.x)  #10,此时把x=100注释掉,对象中没有,从子类的名称空间中查找。
# print(obj1.x)  #将父类的x=10也注释掉attributeerror: 'sub' object has no attribute 'x'


#注意:
obj1.x = 1000#这是给对象添加属性的操作,并不是修改子类的属性
print('子类的名称空间', sub.__dict__)
#子类的名称空间 {'__module__': '__main__', 'x': 100, '__doc__': none}

print('对象的名称空间', obj1.__dict__)
#对象的名称空间 {'x': 1000}

print('父类的名称空间', father.__dict__)
#父类的名称空间 {'__module__': '__main__', 'x': 10, '__dict__': <attribute '__dict__' of 'father' objects>,
# '__weakref__': <attribute '__weakref__' of 'father' objects>, '__doc__': none}

四、派生

  • 派生
'''
派生:
    指的是子类继承父类的属性与方法,并且派生出自己独有的属性与方法。
    若子类中的方法名与父类的相同,优先用子类的。
'''

# #父类
# class foo:
#     def f1(self):
#         print('from foo.f1...')
#
#     def f2(self):
#         print('from foo.f2...')
#
#
# #子类
# class goo(foo):
#
#     #重写(其实python中根本没有重写一说,姑且就称之为重写)
#     def f1(self):
#         print('from goo.f1...')
#
#
#     def func(self):
#         print('from goo.func...')
#
# obj = goo()
# print(obj.f1())  #from goo.f1...
# print(obj.f2())  #from foo.f2...
# print(obj.func())  #from goo.func...


#父类
class foo:
    def f1(self):
        print('from foo.f1...')

    def f2(self):
        print('from foo.f2...')
        self.f1()


#子类
class goo(foo):

    #重写(其实python中根本没有重写一说,姑且就称之为重写)
    def f1(self):
        print('from goo.f1...')


    def func(self):
        print('from goo.func...')

obj = goo()
obj.f2()   #答案是多少?
#'from foo.f2...'  'from goo.f1...'

五、子类继承父类并重用父类的属性的方法

  • 代码
'''
- 子类继承父类,派生出自己的属性与方法,并且重用父类的属性与方法。
'''

'''需求:此时要给子类老师类添加新的属性薪水,给子类中的学生类添加女票'''
#方法一:直接在子类中添加ps(那我还要你父类干嘛)
#父类
# class oldpeople:
#     school = 'oldboy'
#     country = 'china'
#
#     def __init__(self, name, age, sex):
#         self.name = name
#         self.age = age
#         self.sex = sex
#
#
# #子类
# #学生类
# class oldstudent(oldpeople):
#
#     def __init__(self, name, age, sex, girl_friend):
#         self.name = name
#         self.age = age
#         self.sex = sex
#         self.girl_friend = girl_friend
#
#     def chose_course(self):
#         print(f'学生{self.name}正在选课!')
#
#
# #老师类
# class oldteacher(oldpeople):
#
#     def __init__(self, name, age, sex, sal):
#         self.name = name
#         self.age = age
#         self.sex = sex
#         self.sal = sal
#
#     def change_score(self):
#         print(f'老师{self.name}正在修改分数')
#
#
#
# obj1 = oldstudent('yafeng', 18, 'male', '热巴')
# print(obj1.name, obj1.age, obj1.sex, obj1.girl_friend)  #yafeng 18 male 热巴
#
#
# obj2 = oldteacher('tank', 25, 'male', 15000)
# print(obj2.name, obj2.age, obj2.sex, obj2.sal)  #tank 25 male 15000


'''
解决需求: 子类重用父类的属性,并派生出新的属性。
    两种方式:
        1.直接引用父类的__init__为其传参,并添加子类的属性。
        2.通过super来指向父类的属性。
            - super()是一个特殊的类,调用super得到一个对象,该对象指向父类的名称空间。

        注意: 使用哪一种都可以,但不能两种方式混合使用。
'''

#方式一:直接引用父类的__init__为其传参,并添加子类的属性。

# #父类
# class oldpeople:
#     school = 'oldboy'
#     country = 'china'
#
#     def __init__(self, name, age, sex):
#         self.name = name
#         self.age = age
#         self.sex = sex
#
# #子类
# #学生类
# class oldstudent(oldpeople):
#
#     def __init__(self, name, age, sex, girl_friend):
#
#         # 类调用类内部的__init__,只是一个普通函数
#         oldpeople.__init__(self, name, age, sex)
#         self.girl_friend = girl_friend
#
#     def chose_course(self):
#         print(f'学生{self.name}正在选课!')
#
#
# #老师类
# class oldteacher(oldpeople):
#
#     def __init__(self, name, age, sex, sal):
#         oldpeople.__init__(self, name, age, sex)
#         self.sal= sal
#
#     def change_score(self):
#         print(f'老师{self.name}正在修改分数')
#
# obj1 = oldstudent('yafeng', 18, 'male', '热巴')
# print(obj1.name, obj1.age, obj1.sex, obj1.girl_friend)  #yafeng 18 male 热巴
#
#
# obj2 = oldteacher('tank', 25, 'male', 15000)
# print(obj2.name, obj2.age, obj2.sex, obj2.sal)  #tank 25 male 15000


#方式二:2.通过super来指向父类的属性。
#- super()是一个特殊的类,调用super得到一个对象,该对象指向父类的名称空间。

# 父类
class oldpeople:
    school = 'oldboy'
    country = 'china'

    def __init__(self, name, age, sex):
        self.name = name
        self.age = age
        self.sex = sex


#子类
#学生类
class oldstudent(oldpeople):

    def __init__(self, name, age, sex, girl_friend):
        super().__init__(name, age, sex)#注意此时使用super()方法,不用传self
        self.girl_friend = girl_friend
    def chose_course(self):
        print(f'学生{self.name}正在选课!')


#老师类
class oldteacher(oldpeople):

    def __init__(self, name, age, sex, sal):
        super().__init__(name, age, sex)
        self.sal = sal

    def change_score(self):
        print(f'老师{self.name}正在修改分数')



obj1 = oldstudent('yafeng', 18, 'male', '热巴')
print(obj1.name, obj1.age, obj1.sex, obj1.girl_friend)  #yafeng 18 male 热巴


obj2 = oldteacher('tank', 25, 'male', 15000)
print(obj2.name, obj2.age, obj2.sex, obj2.sal)  #tank 25 male 15000

六、经典类与新式类

  • 了解
#!/usr/bin/u/ubv/a python
#_*_ coding:utf8 _*_
'''
经典类与新式类: (了解)
    - 工作中遇不到
    - 面试有可能会问

- 新式类:
    1.凡是继承object的类或子孙类都是新式类。
    2.在python3中所有的类都默认继承object。

- 经典类:
    1.在python2中才会有经典类与新式类之分。
    2.在python2中,凡是没有继承object的类,都是经典类。

'''


class user(object):
    pass

class user():
    x = 10
    pass

class sub(user):
    pass

print(user.__dict__)

七、super严格遵循mro继承顺序原则

  • 了解
'''
调用mro返回的是一个继承序列: (了解知识点)
    super的继承顺序严格遵循mro继承序列。
'''

class father1:
    x = 10
    pass

class father2:
    x = 100
    pass

class sub(father1, father2):
    pass

print(sub.mro())  #[<class '__main__.sub'>, <class '__main__.father1'>, <class '__main__.father2'>, <class 'object'>]
obj = sub()
print(obj.x)  #10


'''
在python3中提供了一个查找新式类查找顺序的内置方法.
    mro(): 会把当前类的继承关系列出来。
'''
# 注意: super()会严格按照mro列表的顺序往后查找
class a:
    def test(self):
        print('from a.test')
        super().test()


class b:
    def test(self):
        print('from b.test')


class c(a, b):
    pass


c = c()
# 检查super的继承顺序
print(c.mro())

# # 去a找,有的话打印,然后super又执行了test,根据mro中查找打印b类中test。

# '''
# from a.test
# from b.test
# '''

八、钻石继承(菱形继承)

  • 了解
'''
多继承情况下造成 “钻石继承”

    mro的查找顺序:
        - 新式类:
            - 广度优先

        - 经典类:
            - 深度优先

面试注意细节:
    - 遇到一个技术 知道是什么,但是不会用,一定要贬低这个技术,觉得很简单,让面试官误以为你很会。
    - 遇到一个技术,不知道是什么,要说我见过,但是忘记怎么用了,我博客里面,回头找一下就行了。
    
    -新式类:广度优先
    - 经典类:深度优先

'''

# 了解:
# 新式类:
class a(object):
    # def test(self):
    #     print('from a')
    pass

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

# f-->d-->b-->e-->c-->a-->object
# print(f.mro())
obj = f()
obj.test()