OOP的三大特征之继承 | 了解组合与继承的原理,以及菱形继承与mro列表

今日内容:


 

OOP的三大特征之一:


 

封装,继承,多态

继承:


 

什么是继承?

  • 继承是一种关系,是描述两个对象之间,什么是什么的关系
  • 在程序中,继承描述的是类和类之间的关系

例如:

  • a继承了b,a就能直接使用b已有的方法和属性;
  • a称之为子类,b称之为父类或者基类

为什么要使用继承?

  • 继承的一方可以直接使用被继承一方的已有的东西
  • 其目的是为了重用已经有的代码,提高重用性

如何使用继承?

继承的基本语法:

class 类名称(父类的名称):
# 在python中,一个子类可以同时继承多个父类
class Base:
    desc = '这是一个基类'
    
    def show_info(self):
        print(self.desc)
        
    def make_money(self):
        print('一天赚一个亿...')
        
        
# 如果不继承Base类        
class SubClass: 
    def make_money(self):
        print('一天赚一百...')     
obj = SubClass()
obj.make_money()  # 一天赚一百
​
​
# 如果继承了Base类     
class SubClass(Base):
    def make_money(self):
        print('一天赚一百...')  
obj = SubClass()
obj.make_money()  # 一天赚一个亿
print(obj.desc)  # 这是一个基类
​
​
# 指定父类Base:
class Subclass()
    pass
obj = SubClass()
# 即使类中什么也没有,也可以使用类中的方法
obj.make_money()  
print(obj.desc)  

抽象:

表现形式:不具体,不清晰,很模糊,看不懂

定义:

  • 将多个子类中相同的部分进行抽取,形成一个新的类,这个过程就是抽象的过程

正确的使用继承:

  • 1.先抽象,再继承
  • 2.继承一个已经现有的类,扩展或是修改原始的功能

继承与抽象:

class Teacher:
    def __init__(self,name,age,gender):
        self.name = name
        self.age = age
        self.gender = gender
        
    def say_hi(self):
        print("name:%s,gender:%s,age:%s" % (self.name,self.gender,self.age))
     
    def teaching(self):
        print('老师教学生,写代码')
        
t1 = Teacher("jack","male",20)
t1.say_hi()
​
​
# 如果不继承Teacher,有冗余代码
class Student:
    def __init__(self,name,gender,age,number):
        self.name = name
        self.age = age
        self.gender = gender
        self.numuber = number
        
    def say_hi(self):
        print("name:%s,gender:%s,age:%s" % (self.name,self.gender,self.age))
    
stu1 = Student("rose","female",18,"xxx01")
stu1.say_hi()
​
​
# 如果继承Teacher
class Student(Teacher):
    pass
​
stu1 = Student("rose","female",18)
stu1.say_hi()
​
​
# 开始抽象
# 抽取老师和学生中相同的类
class Person:
    def __init__(self,name,age,gender):
        self.name = name
        self.age = age
        self.gender = gender
        
    def say_hi(self):
        print("name:%s,gender:%s,age:%s" % (self.name,self.gender,self.age))
        
class Teacher(Person):
    def teaching(self):
        print('老师教学生,写代码')
        
t1 = Teacher("jack","male",20)
t1.say_hi()
        
class Student(Person):
    pass
​
stu1 = Student("rose","female",18)
stu1.say_hi()

属性查找顺序:

先找对象本身的>>>所在类中>>>找父类>>>父类的父类>>>object

# 正常继承
class A:
    text = 'hahaha' 
class B(A):
    text = "heihei" 
b = B()
print(b.text)  # heihei
​
​
# 继承后,B类中pass
class A:
    text = 'hahaha'
class B(A):
    pass
b = B()
print(b.text)  # hahaha
​
​
# 继承后,b给本身赋了值
class A:
    text = 'hahaha'
class B(A):
    text = "heihei"
b = B()
b.text = "xixi"
print(b.text)  # xixi

派生:

定义:

  • 当一个子类中出现了与父类中不同的内容时,这个子类就称之为派生类
  • 通常子类都会写一些新的代码,不可能与父类完全一样,即都是派生类

总结:派生类指的就是子类

覆盖:

定义:

  • 它也称之为重写overrides
# 当子类出现了与父类名称完全一致的属性或方法
class Person:
    def say_hi(self):
        print('hello')
​
class Student(Person):
    def say_hi(self):
        print('hello world!')
        
stu = Student()
stu.say_hi()  # 'hello world' 因为hello被覆盖了

练习:实现一个可以限制元素类型的容器(列表,字典,元祖,集合,字符串)

涉及知识点:我们需要访问父类的append函数来完成真正的存储操作

class MyList(list):
    def __init__(self, element_type):
        # 初始化
        super().__init__(element_type)
        self.element_type = element_type
        
    def append(self, object):
        if type(object) == self.element_type:
            # 这里我们需要访问父类的append函数来完成真正的存储操作
            super(MyList,self).append(object)
        else:
            print('你的element_type not is %s' % self.element_type)
​

# 创建时指定要存储的元素类型
m = MyList(int)
# 当你有需求,是需要在创建对象时,干点什么事儿,那就应该在初始化方法时操作
m.append(l)
print(m[0])
m.append('121212')   

子类中访问父类的内容:


 

语法:

# 方式1:
super(当前类名称,self).你要调用的父类的属性或方法

# 方式2:
super().你要调用的父类的属性或者方法

# 方式3:其实这种方式与继承无关
类名称.你要调用的父类的属性或者方法

注意点:

  • 当你继承一个现有的类,并且你覆盖了父类的init方法时,必须在初始化方法的第一行调用父类的初始化方法,并传入父类所需要的参数

实例:

class Parent:
    text = 'abc'
    def say_something(self):
        print('anything')
        
class Sub(Parent):
    def show_info(self):
        # 访问方式1
        print(super(Sub,self).text)
        super(Sub,self).say_something()
        
        # 最常用的!访问方式2 py3中的新语法(推荐使用)
        print(super().text)
        super().say_something()
        
        # 访问方式3 直接指定类型调用
        print(Parent.text)
        Parent.say_something(self)
        
sub = Sub()
sub.show_info()

初始化方法中必须调用super.py:

class Person:
    def __init__(self,name,gender,age):
        self.name = name
        self.gender = gender
        self.age = age
    def say_hi(self):
        print('name:%s, gender:%s, age:%s' % (self.name, self.gender, self.age))

class Student(Person): def __init__(self,name,gender,age,number): super().__init__(name,gender,age) self.number = number def say_hi(self): super().say_hi() print('number:%s' % self.number)
stu
= Student('rose', 'male', 20, '0001') stu.say_hi()

组合:


 

定义:

  • 它也是一种关系,描述两个对象之间,是什么有什么的关系
  • 将一个对象作为另一个对象的属性(即什么有什么)

组合的目的:

  • 它也是为了重用现有代码

什么时候使用继承:

  • 分析两个类的关系,到底是不是----什么是什么的关系

什么时候使用组合:

  • 如果两个类之间,没有什么太大的关系,完全不属于同类

补充知识点:

  • 组合相比于继承,耦合度更低

例如:学生有手机,游戏中角色拥有的某些装备

class Phone:
    def __init__(self,price,kind,color):
        self.price = price
        self.kind = kind
        self.color = color
    def call(self):
        print('正在呼叫XXX:')
    def send_msg(self):
        print('正在发送短信:')

 
class Student:
    def __init__(self,name,gender,phone):
        self.name = name
        self.gender = gender
        self.phone = phone
    def show_info(self):
        print('name:%s, gender:%s' % (self.name, self.gender))


phone = Phone(1000,'apple','red')
stu1 = Student('rose','male',phone)
stu1.phone.call()  # 正在呼叫XXX:

了解知识点:继承的原理


 

菱形继承与mro列表:

首先要了解python支持多继承

# 讨论一些python支持多继承可能会出现的问题
class A:
    pass
class B:
    pass
class B:
    passclass Test(A,B,C):
    pass
print(Test.mro())  
# [ <class '__main__.Test'>,<class '__main__.A'>,
<class '__main__.B'>,<class '__main__.C'>,object ]

补充: 新式类与经典类

  • python3中任何类都是直接或间接的继承了Object
  • 新式类,任何显式或者隐式地继承自object的类就称之为新式类,python3中全都是新式类
  • 经典类,既不是object的子类,仅仅在python2中出现

新式类与经典类在菱形继承中的体现:

class A:
    j = 1
    pass
class B(A):
    j = 2
    pass
class C(A):
    j = 3
    pass
class D(A):
    j = 4
    pass
​
d = D()
print(d,j)  # 输出4,如果D中j = 2注掉,则输出3;如果C中j = 2注掉,则输出2,依次往上输入

总结:

当出现了菱形继承时:

  • 新式类,先深度,当遇到了共同父类时就广度
  • 经典类,单纯的深度优先
原文地址:https://www.cnblogs.com/zhukaijian/p/11246947.html