Python面向对象编程-继承
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