Python面向对象编程-继承

2021-03-23 16:27

阅读:688

标签:系统   weakref   否则   nap   name   初始   特殊   flag   编译器   

Python面向对象编程-继承
 
资料:
技术图片
 
 
继承 - 语法
  • 单类继承
class 派生类名(基类名): # 另一种说法:子类名(父类名)
    语句块
 
  • 多类继承
class 派生类名(类名1, 基类名2, 基类名3, ......):# 另一种说法:子类名(父类名1,父类名2,父类名3, ......)
    语句块
 

继承 - 属性
  • 子类并不继承父类的私有属性,因此不能访问父类的私有属性。
  • 继承可以层层传递,若当前类的一个属性在当前类内找不到,编译器会查找其父类中是否有,父类的父类中是否有,......,直到查找到最初父类为止。
  • 多类继承的特殊情况
    • 多类继承的层层传递,查找顺序根据从子类-->父类序列的大方向,再在父类序列中从左-->右的顺序查找,直到查找到最初父类为止。
 

继承 - 方法
  • 子类并不继承父类的私有方法,因此不能访问父类的私有方法。
  • 若子类和父类有相同的方法,则子类只访问自己定义的方法,若要访问父类的同名方法需使用super方法。
    • 比如初始化时,需要访问父类的初始化方法,则:
      • super(子类,self).__init__(参数1,参数2,....)
      • 若是Python3,可以是super().__init__(参数1,参数2,....)
      • Python3.x 和 Python2.x 的一个区别是: Python 3 可以使用直接使用 super().xxx 代替 super(Class, self).xxx :
  • 继承可以层层传递,若当前类的一个方法在当前类内找不到,编译器会查找其父类中是否有,父类的父类中是否有,......,直到查找到最初父类为止。
  • 多类继承的特殊情况
    • 多类继承的层层传递,查找顺序根据从子类-->父类序列的大方向,再在父类序列中从左-->右的顺序查找,直到查找到最初父类为止。
 
 

继承 - 初始化
  • 初始化方法1:子类不重写__init__(),此时会自动调用父类的初始化函数。
  • 初始化方法2:子类重写__init__(),系统初始化时将调用子类的初始化函数。
  • 初始化方法3:子类重写__init__(),在其中调用父类的初始化函数,有2种方法调用父类初始化函数:
    • 方法1:super(子类,self).__init__(参数1,参数2,....),或者super().__init__(参数1,参数2,....)
    • 方法2:父类名称.__init__(self,参数1,参数2,...)
 

用例分析
 
用例:烤箱用例实现
技术图片
 
# 基类
class OvanEconomy:
    #类属性
    ovan_type = "OVAN_ECONOMY"
 
 
    def __init__(self):
        # 实例属性
        self.bake_time = 0
        self.temperature = 0
 
 
    def set_baketime(self, bake_time):
        self.bake_time = bake_time
 
 
    def get_baketime(self):
        return self.bake_time
 
 
    def set_temperature(self, temperature):
        self.temperature = temperature
 
 
    def get_temperature(self):
        return self.temperature
 
 
    def bake(self):
        print("--------------------------")
        print("       %s"%(self.__class__.ovan_type))
        print("--------------------------")
        print("bake_time: %d s"%(self.bake_time))
        print("temperature:", self.temperature)
        print("BAKEING......")
 
 
 
 
# 派生类/基类
class OvanAdvanced(OvanEconomy):
    # 类属性
    ovan_type = "OVAN_ADVANCED"
 
 
    def __init__(self):
        # 实例属性
        super().__init__()
        self.temperature_up = 0
        self.temperature_down = 0
 
 
    # 重载
    def set_temperature(self, temperature_up, temperature_down):
        self.temperature_up = temperature_up
        self.temperature_down = temperature_down
 
 
    # 重载
    def get_temperature(self):
        return self.temperature_up, self.temperature_down
 
 
    # 重载
    def bake(self):
        print("--------------------------")
        print("       %s" % (self.__class__.ovan_type))
        print("--------------------------")
        print("bake_time: %d s"%(self.bake_time))
        print("temperature: UP-%d,  DOWN-%d"%(self.temperature_up, self.temperature_down))
        print("BAKEING......")
 
 
 
 
# 派生类
class OvanAppointment(OvanAdvanced):
    # 类属性
    ovan_type = "OVAN_APPOINTMENT"
 
 
    def __init__(self):
        # 实例属性
        super().__init__()
        self.appointment_time = 0
 
 
    # 重载
    def set_appointment_time(self, appointment_time):
        self.appointment_time = appointment_time
 
 
    # 重载
    def get_appointment_time(self):
        return self.appointment_time
 
 
    # 重载
    def bake(self):
        print("--------------------------")
        print("       %s" % (self.__class__.ovan_type))
        print("--------------------------")
        print("bake_time: %d s"%(self.bake_time))
        print("appointment_time: run after %d hours" % (self.appointment_time))
        print("temperature: UP-%d,  DOWN-%d"%(self.temperature_up, self.temperature_down))
        print("BAKEING......")
 
 
 
 
# 派生类
class OvanWindRotary(OvanAdvanced):
    # 类属性
    ovan_type = "OVAN_WIND_ROTARY"
 
 
    def __init__(self):
        # 实例属性
        super().__init__()
        self.wind_rotary_flag = False
 
 
    # 新增
    def on_wind_rotary(self):
        self.wind_rotary_flag = True
 
 
    # 新增
    def off_wind_rotary(self):
        self.wind_rotary_flag = False
 
 
    # 重载
    def bake(self):
        print("--------------------------")
        print("       %s" % (self.__class__.ovan_type))
        print("--------------------------")
        print("bake_time: %d s"%(self.bake_time))
        print("temperature: UP-%d,  DOWN-%d"%(self.temperature_up, self.temperature_down))
        if self.wind_rotary_flag:
            print("BAKEING WITH WIND ROTARY......")
        else:
            print("BAKEING......")
 
 
 
 
 
 
# 测试1:高级烤箱
ovan_advanced = OvanAdvanced()
ovan_advanced.set_baketime(100)
ovan_advanced.set_temperature(100, 200)
ovan_advanced.bake()
 
 
# 测试2:高级烤箱+预约功能
ovan_appointment = OvanAppointment()
ovan_appointment.set_baketime(100)
ovan_appointment.set_appointment_time(5)
ovan_appointment.set_temperature(100, 200)
ovan_appointment.bake()
 
 
# 测试3:高级烤箱+热风旋转烤
ovan_windrotary = OvanWindRotary()
ovan_windrotary.set_baketime(100)
ovan_windrotary.set_temperature(100, 200)
ovan_windrotary.on_wind_rotary()
ovan_windrotary.bake()
 
输出
--------------------------
       OVAN_ADVANCED
--------------------------
bake_time: 100 s
temperature: UP-100,  DOWN-200
BAKEING......
--------------------------
       OVAN_APPOINTMENT
--------------------------
bake_time: 100 s
appointment_time: run after 5 hours
temperature: UP-100,  DOWN-200
BAKEING......
--------------------------
       OVAN_WIND_ROTARY
--------------------------
bake_time: 100 s
temperature: UP-100,  DOWN-200
BAKEING WITH WIND ROTARY......
 
 
 

一些问题说明
 
用例1:对于类公有属性
  • 子类中未定义父类同名的类公有属性,则子类实例访问的是父类定义和赋值的类公有属性
  • 子类中定义了与父类同名的类公有属性,则子类实例访问的是自己定义的类公有属性,父类定义的类公有属性依然存在,需要使用父类名访问。
 
# 基类
class OvanEconomy:
    #类公有属性
    #ovan_type = "OVAN_ECONOMY"
 
 
# 派生类/基类
class OvanAdvanced(OvanEconomy):
    # 子类定义的和父类同名的类公有属性
    ovan_type = "OVAN_ADVANCED"
 
 
# 测试1:屏蔽子类定义的ovan_type后访问该属性
#ovan_type = "OVAN_ADVANCED"
print("测试1:")
print("ovan_advanced.ovan_type:", ovan_advanced.ovan_type)
 
输出:
测试1:
ovan_advanced.ovan_type: OVAN_ECONOMY
 
# 测试2:开放子类定义的ovan_type后访问该属性
print("测试2:")
print("ovan_advanced.ovan_type:", ovan_advanced.ovan_type)
print("OvanEconomy.ovan_type:", OvanEconomy.ovan_type)
 
输出:
测试2:
ovan_advanced.ovan_type: OVAN_ADVANCED
OvanEconomy.ovan_type: OVAN_ECONOMY
 
 
用例2:对于父类的私有类属性和私有用例属性
  • 子类不能继承父类的私有类属性,私有用例属性,因此,理论上也不能访问父类的私有类属性,私有用例属性。
  • 不能访问的原因是父类的私有属性在后台改名了。使用print(dir(父类名))可以查询改名后的私有属性名称,用该名称子类可以访问父类的私有属性。
 
# 基类
class OvanEconomy:
    #类属性
    ovan_type = "OVAN_ECONOMY"
    #类私有属性
    __test1 = "TEST1"
 
 
    def __init__(self):
        # 实例属性
        self.bake_time = 0
        #实例私有属性
        self.__test2 = "TEST2"
 
 
 
 
# 派生类/基类
class OvanAdvanced(OvanEconomy):
    # 类属性
    ovan_type = "OVAN_ADVANCED"
 
 
    def __init__(self):
        # 实例属性
        self.bake_time = 200
        super().__init__()
 
 
# 生成实例
ovan_advanced = OvanAdvanced()
# 打印实例能访问的属性和方法
print(dir(ovan_advanced))
# 访问父类的类私有属性,实例私有属性
print(ovan_advanced._OvanEconomy__test1, ovan_advanced._OvanEconomy__test2)
 
输出:
[‘_OvanEconomy__test1‘, ‘_OvanEconomy__test2‘, ‘__class__‘, ‘__delattr__‘, ‘__dict__‘, ‘__dir__‘, ‘__doc__‘, ‘__eq__‘, ‘__format__‘, ‘__ge__‘, ‘__getattribute__‘, ‘__gt__‘, ‘__hash__‘, ‘__init__‘, ‘__init_subclass__‘, ‘__le__‘, ‘__lt__‘, ‘__module__‘, ‘__ne__‘, ‘__new__‘, ‘__reduce__‘, ‘__reduce_ex__‘, ‘__repr__‘, ‘__setattr__‘, ‘__sizeof__‘, ‘__str__‘, ‘__subclasshook__‘, ‘__weakref__‘, ‘bake_time‘, ‘ovan_type‘]
 
TEST1 TEST2
 
 
 
用例3:关于在子类中重写父类方法的一些使用规则 【可选】
  • 若父类的某个方法在子类中被重写,则该方法在父类的其他方法中不要调用(尤其是父类的初始化__init__()),否则容易出错。
 
以下是出错的例子:
 
# 基类
class base_1:
    nick_name = "农场"
 
    #父类初始化
    def __init__(self, city):
        print("父类初始化开始......")
        self.class_type = "base"
        self.__class_id = 0
        # 这里在实际运行中调用是子类重写的同名方法。
        self.produce_id()
        self.city = city
 
 
    def print_id(self):
        print("CLASS %s ‘s ID: %d"%(self.__class__.__name__, self.__class_id))
 
 
# 派生类,单继承
class derived_1(base_1):
    nick_name = "蔬菜基地"
 
    # 初始化重写
    def __init__(self, city):
        print("子类初始化开始......")
        # 采用super调用父类的初始化函数
        super(derived_1, self).__init__(city)
        # 声明子类自身的__class_id
        self.__class_id = 0
        print("子类初始化结束!")
 
 
    # 重写__class_id的打印方法
    def print_id(self):
        print("CLASS %s ‘s ID: %d"%(self.__class__.__name__, self.__class_id))
 
 
    # 重写__class_id生成方法
    def produce_id(self):
        print("我是子类重写的produce_id()")
        self.__class_id  = random.randint(100000,999999)
 
 
    def test(self):
        print("调用父类的__class_id打印方法打印父类的私有属性:")
        super(derived_1, self).print_id()
 
 
输出:
# 单继承用例----------------------------------
子类初始化开始......
父类初始化开始......
我是子类重写的produce_id()
父类初始化结束!
子类初始化结束!
调用父类的__class_id打印方法打印父类的私有属性:
CLASS derived_1 ‘s ID: 0 # 这个值并未如期望中的改变,因为父类初始化中调用的是子类重写的produce_id方法。
 
 
分析:父类初始化时调用了produce_id()方法,但是调用的是子类重写的方法,因此父类的用例私有属性__class_id并未更新,实际上该调用生成了子类的__class_id属性。
如果把子类__init__()改成:
def __init__(self, city):
print("子类初始化开始......")
# 采用super调用父类的初始化函数
super(derived_1, self).__init__(city)
"""
# self.class_type = "drived"
self.__class_id = 0
"""
self.print_id()
print("子类初始化结束!")
 
则最后的输出为:
# 单继承用例----------------------------------
子类初始化开始......
父类初始化开始......
我是子类重写的produce_id()
父类初始化结束!
CLASS derived_1 ‘s ID: 563690  #父类初始化函数调用的子类produce_id()生成了子类的__class_id属性。
子类初始化结束!
 
 
用例4:关于多继承中的父类用例,方法,子类用例,方法之间的关系【可选】
用例描述:子类重写__init__(),并在初始化前调用父类1的__init__(),父类2的__init__()。两者的__init__()都有对用例的私有属性的初始化操作。
 
# 基类
class base_1:
    nick_name = "农场"
 
 
    def __init__(self, city):
        print("父类1初始化......")
        self.class_type = "base"
        self.__class_id = 0
        self.produce_id()
        self.city = city
 
        # 该类特有的属性,农场面积(亩)
        self.area = 100
        print("父类1初始化结束!")
 
 
    def produce_id(self):
        self.__class_id = random.randint(100000,999999)
        print("父类1的produce_id()------->", self.__class_id)
 
 
    def print_id(self):
        print("父类1的print_id() --->", self.__class_id)
 
 
# 基类
class base_2:
    nick_name = "养殖场"
 
 
    def __init__(self, city):
        print("父类2初始化......")
        self.class_type = "base"
        self.__class_id = 0
        self.produce_id()
        self.city = city
 
        # 该类特有的属性,养殖场牲畜头数
        self.animal = 10000
 
 
        print("父类2初始化结束!")
 
 
    def produce_id(self):
        self.__class_id = random.randint(100000,999999)
        print("父类2的produce_id()------->", self.__class_id)
 
 
    def print_id(self):
        print("父类2的print_id() --->", self.__class_id)
 
 
 
 
# 派生类,多继承
class derived_2(base_1, base_2):
    nick_name = "综合基地"
 
 
    # 初始化重写
    def __init__(self, city1, city2):
        print("子类初始化......")
        # 采用父类类名的方式调用父类的初始化函数
        # 父类1初始化
        base_1.__init__(self, city1)
        base_1.print_id(self)
        # 父类2初始化
        base_2.__init__(self, city2)
        base_2.print_id(self)
 
        # 初始化子类属性
        self.class_type = "drived"
        self.__class_id = -1
 
        print("子类初始化结束!")
 
 
    def print_id(self):
        print("子类的print_id() --->", self.__class_id)
 
 
    def test(self):
        self.print_id()
        base_1.print_id(self)
        base_2.print_id(self)
 
 
 
 
# 多继承用例
print("\n\n# 多继承用例----------------------------------")
instance2 = derived_2("上海", "南京")
print("city: ", instance2.city)
print("animal: ", instance2.animal)
print("area: ", instance2.area)
print("class_type: ", instance2.class_type)
instance2.test()
 
 
输出:
# 多继承用例----------------------------------
子类初始化......
父类1初始化......
父类1的produce_id()-------> 832192
父类1初始化结束!
父类1的print_id() ---> 832192
父类2初始化......
父类1的produce_id()-------> 791476
父类2初始化结束!
父类2的print_id() ---> 0
子类初始化结束!
city:  南京
animal:  10000
area:  100
class_type:  drived
子类的print_id() ---> -1
父类1的print_id() ---> 791476
父类2的print_id() ---> 0
 
 
分析:
在子类中,通过父类名前缀,可以准确的调用父类的用例公有方法。
但是,在每个父类各自的方法调用中,若是调用的其他父类,子类的同名方法,则实际调用的未必本身类中定义的方法,有以下2种情况:
  • 情况1:子类没有重写同名方法,则根据子类继承多个父类时,从左至右排序的第一个父类的同名方法。
           如本列中的父类2的__init__()方法中调用的produce_id()方法就来自于父类1,而该方法实际修改的是父类1的私有变量__class_id,因此,调用该方法后,父类
           2的私有变量__class_id并未修正,修正的是父类1的私有变量。
  • 情况2:子类重写同名方法,则调用的是子类重写的方法。
           如本列中,若子类重写了produce_id()方法,则在父类1,父类2的初始化时,调用的都是子类重写的produce_id()方法,修改的将会是子类的私有变量__class_id,此时的输出结果为:
# 多继承用例----------------------------------
子类初始化......
父类1初始化......
子类的produce_id()-------> 254232
父类1初始化结束!
父类1的print_id() ---> 0
父类2初始化......
子类的produce_id()-------> 428122
父类2初始化结束!
父类2的print_id() ---> 0
子类初始化结束!
city:  南京
animal:  10000
area:  100
class_type:  drived
子类的print_id() ---> -1
父类1的print_id() ---> 0
父类2的print_id() ---> 0
 
 

 
 

Python面向对象编程-继承

标签:系统   weakref   否则   nap   name   初始   特殊   flag   编译器   

原文地址:https://www.cnblogs.com/JacquelineQA/p/13835679.html


评论


亲,登录后才可以留言!