类和对象

多态之前先看三个内置(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视频内容进行了梳理,修正了发现的错误、对代码做了些许优化、替换掉视频中的英文注释、替换掉国内不能访问的资源……敬请关注,谢谢