课程:Python面向对象
进度:day2
上次内容回顾
1. 面向对象:以对象为中心
2. 三大特性:封装、继承、多态
3. 如何定义类、如何实现继承
class 类名称(父类列表):
# 构造函数
# 其它成员函数
今天的内容
1. 多态:相同的方法,不同的表现(重点)
1)事物除了共性外,还具有各自的特性(个性)
多态就用来处理事物的个性
2)多态能够在不影响父类行为(不修改父类代码)
情况下,进行功能的扩展和变更
3)使程序(软件设计)更具灵活性
2. 方法的重写:父类中定义过的方法子类中重新定义
(重点)
3. 多态的实现:通过在子类中重写父类的方法实现多态(重点)
(重点)
课堂练习:
编写一个手机类(Phone)
属性:名称(name), 价格(price),
CPU, 屏幕尺寸( screen_size)
方法:开机(startup),关机(shutdown)
打电话(call), 发短信(send_msg)
并编写测试代码
见 phone.py
1 # phone.py 2 # 手机类 3 import time 4 class Phone: 5 def __init__(self, name, price, 6 CPU, screen_size): 7 self.name = name 8 self.price = price 9 self.CPU = CPU 10 self.screen_size = screen_size 11 12 def startup(self): 13 print("正在开机") 14 time.sleep(2) 15 print("开机成功") 16 17 def shutdown(self): 18 print("正在关机") 19 time.sleep(2) 20 print("关机成功") 21 22 def call(self, phone_no): 23 print("正在拨号") 24 time.sleep(1) 25 print("正在和%s通话" % phone_no) 26 27 def send_msg(self, phone_no, msg): 28 print("正在向%s发送信息..." % phone_no) 29 time.sleep(2) 30 print("【%s】发送成功" % msg) 31 32 def __del__(self): # 析构方法 33 print("__del__方法被调用") 34 35 def fun(): 36 phone = Phone("华为",1999.99, 37 "双核2G", 5.5) 38 print("fun()函数退出") 39 40 if __name__ == "__main__": 41 myphone = Phone("华为",1999.99, 42 "双核2G", 5.5) 43 fun() 44 print("程序退出") 45 # myphone程序退出时被销毁 46 47 48 49 myphone.startup() #启动 50 myphone.call("13512345678") #打电话 51 myphone.send_msg("13512345678","你好") 52 myphone.shutdown() #关机phone.py
4. 面向对象的技术特性
1)构造和析构
a)构造方法(重点)
- 名称:在Python中,固定为__init__
- 作用:是为对象创建属性,并且赋予初始值
- 调用时机:对象被创建(实例化)自动调用
- 如果在类中未定义,Python会给出默认的构造方法
- 在继承关系中,如果父类定义了__init__方法
在子类的构造方法中,必须要调用父类的构造方法
b)析构方法
- 名称:__del__
- 调用时机: 对象被销毁时自动调用
del obj, 局部变量退出作用于,程序退出
- 作用:对象销毁时,执行清理操作
2)多重继承:一个类有多个父类
- 如何实现多继承: 定义类的时候,继承自多个父类,
父类之间用逗号隔开
- 语法格式:
class 类名称(父类1, 父类2, ......):
语句块
- 通过多重继承,子类具有所有父类的特征和行为
- 如果多个父类具有相同的方法(或属性名称),通过
类的__mro__属性中记录的顺序进行方法查找
MRO(Method Resolution Order)
总的原则按照由下至上、从左至右(继承列表中前后)
进行查找,直到object类,如果都没有找到就报错
1 # mul_inh.py 2 # 多重继承示例 3 # 超人类继承自战士、飞行者、喷或者 4 # 三个类 5 class Fighter: #战士类,默认继承自object 6 def fight(self): 7 print("我能战斗") 8 9 def roar(self): #吼叫 10 print("战士吼叫:嗷嗷嗷") 11 12 class Flyer: #飞行者类 13 def fly(self): 14 print("我能飞行") 15 16 def roar(self): #吼叫 17 print("飞行者吼叫:呜呜呜") 18 19 class Firer: # 喷火者 20 def fire(self): 21 print("我能喷火") 22 23 # 超人类,继承自Fighter,Flyer,Firer父类 24 class SuperMan(Flyer,Fighter,Firer): 25 pass 26 27 if __name__ == "__main__": 28 super_man = SuperMan() # 实例化超人对象 29 super_man.fight() # 超人战斗 30 super_man.fly() # 超人飞行 31 super_man.fire() # 超人喷火 32 super_man.roar() # 超人吼叫?? 33 # Mehtod Resolution Order 34 # 决定调用哪一个类的方法 35 # print(SuperMan.__mro__) 36 print(SuperMan.__bases__) # __bases__绑定父类 37 print(Fighter.__base__) 38 print(Flyer.__base__) 39mul_inh.py
3)object类:所有类的总父类,如果定义类时候,没有
指定父类,则默认从object类继承
类的父类可以通过__base__属性查看
4)super()和issubclass()
- super()函数
作用:返回绑定的父类
有时候需要显式调用父类方法
就使用super()函数
格式:super(type, ojb) 返回obj对象父类
super() 返回当前对象父类
只能在类的方法中调用
- issubclass: 判断一个类是否是另一个
类的子类,如果是返回True
如果不是返回False
格式:issubclass(cls, class_or_tuple)
5. 函数重写(重点,理解原理)
1)对象转字符函数的重写
- str:将对象转换成人阅读的字符串
重写__str__()
- repr: 返回字符串,给Python解释器阅读
通过eval(repr(obj))能够还原obj
对象,重写__repr__()
- 当调用str(obj)将对象转换成字符串
时,其搜索顺序为:
+ 优先查找obj.__str__()方法(包括父类)
+ 如果上一步不存在,则调用obj.__repr__()
+ 如果上一步不存在,则调用object.__repr__()
1 2 class A: 3 def __init__(self, name): 4 self.name = name 5 6 def __str__(self): # 重写__str__函数 7 return "name = %s" % self.name 8 9 class B(A): 10 def __init__(self, name, id): 11 super().__init__(name) # 调用父类构造方法 12 self.id = id # id属性被创建 13 14 def __repr__(self): # 重写__repr__方法 15 # 返回例如"B('Jerry','0001')" 16 return "B('%s','%s')" % (self.name, self.id) 17 18 def __str__(self): # 重写__str__函数 19 return "name=%s,id=%s"%(self.name,self.id) 20 21 b = B("Jerry", "0001") # ==》 22 print(b) 23 24 # str_obj = repr(b) 25 # print(str_obj) 26 # new_obj = eval(str_obj) # 通过调用eval函数还原对象 27 # print(new_obj) 28 29 # print(b) # B类中重写__str__方法,所以可以直接打印 30 # str_obj = str(b) # 将b对象转换成字符串 31 # print(str_obj) 32 33 #super(B, b).print() # 根据对象b找到父类,并调用父类的print 34 # print(issubclass(B, (A,object))) # True 35 # print(issubclass(B,object)) # True 36 # print(issubclass(A, B)) # Falsea.py
2)内建函数重写
- abs()函数:重写__abs__()
- len()函数: 重写__len__()
- reversed()函数: 重写__reversed__()
- round()函数:重写__round__()
课堂练习:重写__len__()和__round__()
重写__reversed__()
示例见my_list.py
1 # mylist.py 2 # 自定义列表 3 class MyList: 4 def __init__(self, iterable=[]): 5 self.data = [x for x in iterable] 6 7 # 重写__abs__()函数 8 def __abs__(self): 9 #对每个元素求绝对值,用产生的结果实例化一个MyList对象 10 return MyList(abs(x) for x in self.data) 11 12 def __str__(self): # 重写__str__()函数 13 ret = "" 14 for x in self.data: 15 ret += str(x) # 将元素由数字转换成字符串 16 ret += " " 17 return ret # 返回结果串 18 19 def __len__(self): # 重写__len__函数,返回元素个数 20 return len(self.data) 21 22 # 对每个元素求近似值,并实例化对象返回 23 def __round__(self): 24 return MyList(round(x,2) for x in self.data) 25 26 # 重写__reversed__,倒序 27 def __reversed__(self): 28 tmp = [] 29 x = len(self.data) - 1 # 最后元素下标 30 while x >= 0: 31 tmp.append(self.data[x]) 32 x -= 1 # 计数器减1 33 return MyList(tmp) # tmp是经过倒序的可迭代对象 34 35 def __add__(self, other): # L1 + L2 36 return MyList(self.data + other.data) 37 38 def __mul__(self, value): # L1 * 2 39 return MyList(self.data * value) 40 41 def __eq__(self, other): # 重载==运算符 42 len1 = len(self.data) # 取data属性长度 43 len2 = len(other.data) # 取另一个对象data的长度 44 if len1 != len2: # 长度不相等,无需比较 45 return False 46 47 for i in range(0, len1): 48 if self.data[i] != other.data[i]: 49 return False #只要出现元素不相等,返回False 50 return True 51 52 def __ne__(self, other): # 重载!=运算符 53 return not (self == other) 54 55 def __gt__(self, other): 56 len1 = len(self.data) # 取data属性长度 57 len2 = len(other.data) # 取另一个对象data的长度 58 min_len = min(len1, len2) # 获得循环上限 59 60 for i in range(min_len): # 以长度短的作为循环上限 61 if self.data[i] == other.data[i]: 62 continue 63 elif self.data[i] > other.data[i]: 64 return True 65 elif self.data[i] < other.data[i]: 66 return False 67 if len1 == len2: #相等 68 return False 69 elif len1 > len2: #前面的元素相等,长度比对方大 70 return True 71 else: 72 return False 73 74 def __neg__(self): #重载符号运算符 75 return MyList(-x for x in self.data) 76 77 def __contains__(self, e): # in/not in运算符重载 78 print("__contains__()被调用") 79 return e in self.data 80 81 def __getitem__(self, i): # 重载[]取值操作 82 return self.data[i] 83 84 def __setitem__(self, i, value):#重载[]赋值操作 85 self.data[i] = value 86 87 def __delitem__(self, i): #重载[]删除操作 88 del self.data[i] 89 90 if __name__ == "__main__": 91 L = MyList([-1, 2, -3, 4, -5]) 92 print(L[0]) # 索引取值 93 L[2] = 333 # 通过索引设置值 94 print(L) 95 del L[3] # 删除 96 print(L) 97 98 # print(2 in L) # True 99 # print(4 not in L) # False 100 101 #print(-L) # 对对象执行负号运算 102 103 # L1 = MyList([1,3,5]) 104 # L2 = MyList([1,3,5,7]) 105 # print(L1 > L2) 106 # L = MyList([-1, 2, -3, 5.6789]) 107 # L3 = MyList([3,4,5]) 108 # print(L + L3) 109 # print(L * 2) 110 # print(2 * L) 111 112 # print(reversed(L)) # 对象元素逆序并打印 113 # print(len(L)) # 打印返回的长度 114 # print(round(L)) # 打印新产生的对象 115 # # 每个元素都是原对象元素的近似值 116 # L2 = abs(L) # 重写了__abs__()函数,支持abs表达式 117 # print(L2) # 重写了__str__()函数,所以支持print()my_list.py
3)数值转换函数重写
- int(): 重写__int__()
- float(): 重写__float__()
- complex(): 重写__complex__()
- bool: 重写__bool__()
示例见my_number.py
1 # my_number.py 2 # 自定义数字类型 3 # 数值转换函数重写示例 4 class MyNumber: 5 def __init__(self, value): 6 self.data = value #值,字符串 7 8 def __int__(self): 9 return int(float(self.data)) 10 11 def __float__(self): 12 return float(self.data) 13 14 def __complex__(self): 15 return complex(self.data) 16 17 if __name__ == "__main__": 18 num = MyNumber("123.45") 19 print(getattr(num, "data")) # "123.45" 20 print(num.data) # "123.45" 21 22 setattr(num, "data", "456.78") 23 print(getattr(num, "data")) #456.78 24 25 print(hasattr(num, "data")) #True 26 print(hasattr(num, "aaaa")) #False 27 28 delattr(num, "data") #删除data属性 29 print(hasattr(num, "data")) #False 30 31 # print(int(num)) #将num对象转换成int 32 # print(float(num)) 33 # print(complex(num))my_number.py
6. 属性管理函数:操作对象属性
1)getattr: 获取对象属性值
- 格式: getattr(obj, name[,default])
- 作用: getattr(x, "y") 等同于
x.y表达式
2)setattr: 设置对象属性值
- 格式:setattr(obj, name, value)
- 作用:setattr(x, "y", "zzz")等同于
x.y = "zzz"表达式
3)hasattr: 判断有没有某个属性值
- 格式:hasattr(obj, name)
- 返回:布尔类型,有-True 没有-False
4)delattr: 删除对象某个属性值
- 格式:delattr(obj, name)
- 作用:delattr(x, "y")等同于
del x.y 表达式
作业:
1)编写一个账户类(Account)
属性:账号(acct_no), 户名(acct_name),
开户日期(reg_date),
账户类型(acct_type)(借记卡/贷记卡),
状态(acct_status)(正常,挂失,冻结,注销)
余额(balance)
方法:存款(diposite)
取款(draw)
冻结(freeze), 解冻(unfreeze)
挂失(report_loss),解挂失(relieve_loss)
修改账户信息(modify_acct_info)
2)编写测试代码
1 # account.py 2 # 账户类 3 dict_status = { #状态字典 4 0:"正常",1:"冻结",2:"挂失",3:"销户" 5 } 6 dict_type = {0:"借记账户", 1:"贷记账户"} 7 8 class Account: 9 def __init__(self, acct_no, acct_name, 10 reg_date, acct_type, 11 acct_status, balance): 12 self.acct_no = acct_no #账号 13 self.acct_name = acct_name #户名 14 self.reg_date = reg_date #开户日期 15 self.acct_type = acct_type #类型 16 self.acct_status = acct_status #状态 17 self.balance = balance #余额 18 19 def diposite(self, amt): #存款 20 print("存款前余额:%.2f"%self.balance) 21 self.balance += amt #修改余额 22 print("存款后余额:%.2f"%self.balance) 23 24 def draw(self, amt): #取款 25 if self.acct_status == 0: #正常 26 if self.balance < amt: #余额不足 27 print("余额不足") 28 return 29 else: #余额充足 30 print("取款前余额:%.2f" % self.balance) 31 self.balance -= amt #修改余额 32 print("取款后余额:%.2f" % self.balance) 33 else: #非正常状态 34 print("状态不允许取款") 35 36 def freeze(self): # 冻结 37 if self.acct_status != 0: 38 print("状态不允许冻结") 39 return 40 self.acct_status = 1 #状态置为冻结 41 print("账户已冻结") 42 43 def unfreeze(self): #解冻 44 if self.acct_status != 1: #判断已冻结 45 print("状态不允许解冻") 46 return 47 self.acct_status = 0 #状态置为正常 48 print("账户已解冻") 49 50 def __str__(self): #重写__str__() 51 # 将状态转换为对应的字符串 52 status = dict_status[self.acct_status] 53 # 将类型转换为对应的字符串 54 type = dict_type[self.acct_type] 55 ret="账号:%s,户名:%s,开户日期:%s,类型:%s,状态:%s,余额:%.2f"\ 56 %(self.acct_no, self.acct_name, \ 57 self.reg_date, type, \ 58 status, self.balance) 59 return ret 60 61 if __name__ == "__main__": 62 acct = Account("6223450001", "张大大", 63 "2018-01-01", 0, 0, 2000.00) 64 acct.diposite(1000.00) #存款 65 acct.draw(500) #取款 66 print(acct) #打印账户信息 67 acct.freeze() #冻结 68 print(acct) #打印账户信息 69 acct.unfreeze() #解冻 70 print(acct) #打印账户信息 71 72 73 74account.py