多态之前先看三个内置(build_in)函数,内置函数是python自带的、可以直接拿来用的函数。
首先是getattr(类或对象, 属性或方法函数, 缺省值),获得类和对象的属性或方法函数:
print(getattr(employee1, 'masterwork', '只提供了作家的代表作'))
# 对象employee1没有叫masterwork的属性或方法函数,故输出:只提供了作家的代表作
print(getattr(Employee, 'masterwork', '只提供了作家的代表作'))
# 类Employee没有叫masterwork的属性或方法函数,故输出:只提供了作家的代表作
print(getattr(empWriter1, 'masterwork', '只提供了作家的代表作'))
# 对象empWriter1有属性masterwork,将属性值The adventure of Tom Sawyer输出
print(getattr(Writer, 'masterwork', '只提供了作家的代表作'))
# 类Writer生成的对象有属性masterwork,输出:<property object at 0x105d30b38>
print(getattr(Employee, 'employeeNum', '无法提供雇员个数'))
# 类Employee有类属性employeeNum记录雇员人数,输出为5
print(getattr(empWriter1, 'employeeNum', '无法提供雇员个数'))
# 对象empWriter1所属的类有类属性employeeNum,输出为5
print(getattr(empWriter1, 'info_summary', '没有info_summary属性或方法函数'))
# 对象empWriter1有方法函数info_summary,输出:<bound method Writer.info_summary of <__main__.Writer object at 0x109b23470>>
print(getattr(empWriter1, 'info_summary', '没有info_summary属性或方法函数').__name__)
# 把对象empWriter1方法函数info_summary的名字info_summary输出,用__name__可以提取出类或方法函数的名字
接下来是设置属性值的setattr(对象):
setattr(employee1, 'fname', 'Frodo')
# 设置属性employee1.fname,效果同employee1.fname = ‘Frodo’
setattr(employee1, 'fname', 88)
# 输入的属性值非法,按照fname赋值函数的设定返回“名必须是字符串”的提示
setattr(Employee, 'raiseAmount', 1.05)
# 将类属性值改为1.05
setattr(employee1, 'nickname', 'newAddAttribute')
# 给employee1增加了一个自己独有的属性nickname,值设为'newAddAttribute'
判断类和对象是否有某个属性或方法:
print(hasattr(empLeader1, 'subordinates'))
# 返回True,因为对象empLeader1确实有属性subordinates
print(hasattr(empWriter1, 'subordinates')) # 返回False
print(hasattr(employee1, 'add_sub'))
# 返回False,因为employee1没有方法函数add_sub()
print(hasattr(empLeader1, 'add_sub')) # 返回True
现在看从继承过来的多态,先加一个调整类属性raiseAmount的函数set_raiseAmount():
class Employ(object):
……
class Writer(Employee):
……
class Leader(Employee)
……
def set_raiseAmount(obj, value):
# 两个形参,第一个接收类,第二个接收数字,这里不对对接收的实参的“合法性”做判断
if hasattr(obj, 'raiseAmount'):
# 判断类obj是否有叫做raiseAmount的属性
obj.raiseAmount = value
# 调整obj类属性raiseAmount的值
print('调整后{}类的增长率变为{}'.format(obj.__name__, obj.raiseAmount))
# 输出调整后的结果,obj.__name__可以把类的名字提出来
else:
print('没有增长系数可控调整')
……
set_raiseAmount(Employee, 1.05) (1)
set_raiseAmount(Writer, 1.08) (2)
set_raiseAmount(Leader, 1.2) (3)
从(1)~(3)对set_raiseAmount()的调用可以看到多态性,第一个参数不仅可以是Employee类,还可以是Writer和Leader类。而Leader类自己甚至都没有属性raiseAmount,它的raiseAmount类是从父类Employee继承过来的,可见Employee类和它的之类都可以用这个函数设置属性。
静态语言使用变量必须先定义再使用。python是动态语言,变量无需定义就可以赋值。变量的类型在赋值时由所赋的值决定,值变了变量类型也跟着改变。继承对静态语言的多态是必须的,而对Python这样的动态语言,多态则无需建立在继承的基础上。
先对Leader类的对象方法函数info_summary()添加一个判断,判断下属列表是否为空,不为空才提取下属的名字,否则下属列表为空还坚持提取名字会导致程序出错。
……
class Leader(Employee):
……
def info_summary(self): # 自己的汇总信息函数
subNameList = [] # 下属名字列表初值为空
if self.subordinates: # 如果下属列表不为空
for sub in self.subordinates: # 遍历列表,下属姓名添加进列表
empNameList.append(emp.fullname)
……
加一个与Employee类及其子类无关的鸭子类,再加一个输出对象信息的普通函数:
class Employee(object):……
class Writer(Employee):……
class Leader(Employee):……
class Duck(object): # 定义一个鸭子类
def info_summary(self): # 信息汇总,也可以做成静态方法函数
return ‘门前大桥下游过一群鸭,快来快来数一数,二四六七八’
……
def output(obj): (7)
if hasattr(obj, 'infoSummary'):
# 判断对象obj是否有infoSummary的属性或方法函数
print(type(obj).__name__, obj.infoSummary())
# type(obj)获得对象所属的类,.__name__提取出类的名字
else:
print('无可奉告)
……
output(employee1) (1)
output(empWriter1) (2)
output(empLeader1) (3)
mcdonaldDuck = Duck() (4)
output(mcdonaldDuck) (5)
output(3.1415926) (6)
(1)~(3)跟前面一样,是在继承基础上的多态,(2)的输出如下:
Writer Mark Twain, 8000
This email address is being protected from spambots. You need JavaScript enabled to view it.
The adventure of Tom Sawyer
(1)(3)格式类似,内容略有差别。
有趣的是(5),Duck类(7)与Employee类及其之类无任何关联,然而它的对象mcdonaldDuck(4)却也可以作为output()的实参,只因为它也有info_summary()的方法函数。(5)的输出为:
Duck 门前大桥下游过一群鸭,快来快来数一数,二四六七八
(6)的输出为:无可奉告,因为3.1415926是float类的一个对象,没有叫做info_summary的属性或方法对象,所以就按照output()的设定输出:无可奉告。
python多态使用的是鸭子理论,即如果走起路来鸭子样地一摇一摆,叫起来嘎嘎嘎,那我们就认为它是一只鸭子。从output()这个函数的角度看就是:我不管你是什么类的对象,只要你有个info_summary()的方法函数,就输出汇总信息;没有就输出“无可奉告”。
即将推出的Python ABC教程对PythonABC视频内容进行了梳理,修正了发现的错误、对代码做了些许优化、替换掉视频中的英文注释、替换掉国内不能访问的资源……敬请关注,谢谢