橄榄球教练Roger,拿出了自己的数据结构,我们的队员除了速度训练,还需要进行力量的练习。既然你的类表现的不错,我能不能用呢?
loren,2011-11-3,270,3.59,4.11,3:11,3:23,4-10,3-23,4:10,4.21,4-21
首先我们复习一下昨天的部分
第一我们先打开文件,读取Roger教练记录的数据,这需要一个函数
def get_coach_data(filename):
with open(filename) as f:
line = f.readline()
return line.strip().split(',')
为了方便处理多人数据,我们学习了一个新的概念,类
class Athlete:
def __init__(self,a_name,a_dob=None,a_times=[]):
self.name = a_name
self.dob = a_dob
self.times = a_times
def top3(self):
return sorted(set([self.sanitize(t) for t in self.times]))[0:3]
def sanitize(self,time_string):
if '-' in time_string:
splitter = '-'
elif ':' in time_string:
splitter = ':'
else:
return (time_string)
(mins,secs) = time_string.split(splitter)
return (mins+'.'+secs)
在以上代码中
类的主体结构是
class 类名:
def __init__(self,参数1,参数2,etc.):
self.属性名 = 参数1
self.属性名 = 参数2
def 方法名(self,参数1,参数2,etc.):
#方法内容
return #return并不是必须的部分,但方法要有内容
来看看如何完成Roger教练的新需求吧,一样为了快速处理大量数据,我们需要一个类来作为数据模板,但是和昨天不同的是,明显Athlete类已经不能满足我们的需求了,但难道要再打一段很长很长的Rugby类吗?
class Rugby:
def __init__(self,a_name,a_dob=None,a_times=[],a_squat):
self.name = a_name
self.dob = a_dob
self.times = a_times
self.squat = a_squat
def top3(self):
return sorted(set([self.sanitize(t) for t in self.times]))[0:3]
def sanitize(self,time_string):
if '-' in time_string:
splitter = '-'
elif ':' in time_string:
splitter = ':'
else:
return (time_string)
(mins,secs) = time_string.split(splitter)
return (mins+'.'+secs)
明显代码重复性太高了,完全不符合简洁性,所以我们需要使用继承
Athlete类就是父类
Rugby类就是子类
子类继承了父类所有的属性和方法,同时子类还可以拥有更多的属性和方法
#定义橄榄球运送员类
class Rugby(Athlete):
def __init__(self,a_name,a_bod,a_squat,a_times):
#调用父类__init__
Athlete.__init__(self,a_name,a_bod,a_times)
#深蹲次数
self.squat = a_squat
# 继承后下面两个函数就在Rugby类中,只是看不到而已
# def top3(self):
# return sorted(set([self.sanitize(t) for t in self.times]))[0:3]
# def sanitize(self,time_string):
# if '-' in time_string:
# splitter = '-'
# elif ':' in time_string:
# splitter = ':'
# else:
# return (time_string)
# (mins,secs) = time_string.split(splitter)
# return (mins+'.'+secs)
如果这样,只需要四行代码就可以继承Athlete的所有内容,外加一个新的属性squat
loren = get_coach_data('mywork/loren.txt')
rugby = Rugby(loren.pop(0),loren.pop(0),loren.pop(0),loren)
print('姓名:%s,生日:%s,深蹲:%s个,最块的3次成绩:%s' %(rugby.name,rugby.dob,rugby.squat,rugby.top3()))
关于类
定义:class 子类名(父类名):
情况1,如果子类有新增的属性,那么需要在子类__init方法中,调用父类的__init__
情况2,如果子类没有新增的属性,子类不需要写__init__方法
使用: 对象名 = 子类名(参数)
继承的好处:代码重用,升级功能(重写),新增功能(新的方法)
我们已经学会了如何给子类添加属性,那么关于新的方法呢?
平常需要新的方法时,直接在新的子类中define一个就可以,但如果我们的新方法名和父类的一个方法相同时,新方法会覆盖掉旧方法,也就是方法重写。
子类方法与父类方法完全相同,子类若重写了父类的方法,则子类对象调用方法时就是调用的自己类中重新的方法,但放心,方法重写不会影响父类方法。
Roger教练不需要队员的三个最快成绩,相反,他需要三个最慢成绩,甚至不需要数据去重,那我们应该怎么写呢?
class Rugby(Athlete):
def __init__(self,a_name,a_bod,a_squat,a_times):
Athlete.__init__(self,a_name,a_bod,a_times)
self.squat = a_squat
def top3(self):
return sorted([self.sanitize(t) for t in self.times])[-3:]
这样一来,Rugby内的top3方法就变成了:
将数据规范化,写进一个新列表,然后排序,取出末三位
又来10个各种各样的运动员,他们的top3完全不同,代码看起来是这样的。
class OtherAthlete(Athlete):
def __init__(self,a_name,a_bod,a_squat,a_times):
Athlete.__init__(self,a_name,a_bod,a_times)
self.squat = a_squat
def top3(self):
return sorted([self.sanitize(t) for t in self.times])[0:3]
我需要为每种远动员定义一个子类,调用的时候是一堆重复的代码。
mark1 = get_coach_data('mywork/mark.txt')
mark2 = get_coach_data('mywork/mark1.txt')
mark3 = get_coach_data('mywork/mark2.txt')
mark1 = OtherAthlete(mark1.pop(0),mark1.pop(0),mark1.pop(0),mark1)
mark2 = OtherAthlete(mark2.pop(0),mark2.pop(0),mark2.pop(0),mark2)
mark3 = OtherAthlete(mark3.pop(0),mark3.pop(0),mark3.pop(0),mark3)
print('姓名:%s,生日:%s,深蹲:%s个,最快的3次成绩:%s' %(mark1.name,mark1.dob,mark1.squat,mark1.top3()))
print('姓名:%s,生日:%s,深蹲:%s个,最快的3次成绩:%s' %(mark2.name,mark2.dob,mark2.squat,mark2.top3()))
print('姓名:%s,生日:%s,深蹲:%s个,最快的3次成绩:%s' %(mark3.name,mark3.dob,mark3.squat,mark3.top3()))
这样的重复代码很麻烦,Python作为面向对象语言的三大特征——封装,继承,多态
多态的体现就可以解决这个问题
我们将需要子类做的事情放进一个函数中
def print_rugby(athlete):
print(athlete.name)
print(athlete.dob)
print(athlete.squat)
print(athlete.top3())
这个函数的参数是实例化后的父类,每当我们需要调用子类的组成部分的时候,只需要调用print_rugby函数,并且将子类作为参数放进去
全部的代码是这样的
loren = get_coach_data('mywork/loren.txt')
mark = get_coach_data('mywork/mark.txt')
loren = Rugby(loren.pop(0),loren.pop(0),loren.pop(0),loren)
mark = OtherAthlete(mark.pop(0),mark.pop(0),mark.pop(0),mark)
#新定义一个函数,将父类作为一个参数,方便重复使用子类
#称坐多态
def print_rugby(athlete):
print(athlete.name)
print(athlete.dob)
print(athlete.squat)
print(athlete.top3())
#具体怎么使用决定于子类
print_rugby(loren)
print_rugby(mark)
输出结果
多态性:一个事物多种形态
上面例子中print_rugby的参数athlete,athlete.name,athlete.top3()的行为由athlete的子类决定。
多态的好处是:减少重复代码,分离经常改变的代码与不经常改变的代码,使得代码可维护性提高。
方便创建多个对象
#如何优化创建对象的代码呢?
#将大段代码的变化,转为参数的变化
def obj_factory(name,filename):
with open(filename) as f:
line = f.readline()
templ = line.strip().split(',')
if name == 'r':
return Rugby(templ.pop(0),templ.pop(0),templ.pop(0),templ)
elif name == 'oa':
return OtherAthlete(templ.pop(0),templ.pop(0),templ.pop(0),templ)
oa = obj_factory('oa','mywork/mark.txt')
print(oa.name)
一个子类可以继承多个父类的属性,代码的复用性越高,看起来就越简洁,而不是一团浆糊
class Father():
def talk(self):
print("---爸爸的表达能力---")
class Mather():
def smart(self):
print("---妈妈聪明的头脑---")
class Child(Father,Mather):
pass
child1 = Child()
child1.talk()
child1.smart()
切记,多个父类在创建之时就应该注意,不应该拥有相同的方法名,不然多继承时会出现问题
在每个需要Athlete的时候,我都需要把Athlete类的定义复制粘贴要用的代码上方。
有没有什么办法可以让代码主体更加简洁呢?
import sys
sys.path.append('mywork')
# import athlete
# print(dir(athlete))
from athlete import *
import sys
导入sys模块 sys.path.append(‘work’)
将模块athlete.py添加到模块搜索路径
from athlete import *
导入athlete模块,使用athlete模块下的所有代码
如果有一个模块mymodule.py中也包含get_coach_data函数,该怎么区分这两个函数呢?
import sys
sys.path.append('mywork')
from p1.mymodule import *
from p2.mymodule import *
import p1
import p2
p1.mymodule.demo()
p2.mymodule.demo()
要写清是哪个文件夹下的哪个文件里的demo()函数