定制数据对象
现有类似这样的数据
Sarah Sweeney,2002-6-17,2:58,2.58,2:39,2-25,2-55,2:54,2.18,2:55,2:55,2:22,2-21,2.22
要怎样才能更好地处理数据呢?
def sanitize(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)
def get_coach_data(filename):
try:
with open(filename) as f:
data = f.readline()
return(data.strip().split(','))
except IOError as ioerr:
print('File error: ' + str(ioerr))
return(None)
sarah = get_coach_data('sarah2.txt')
(sarah_name, sarah_dob) = sarah.pop(0), sarah.pop(0)
print(sarah_name + "'s fastest times are: " +
str(sorted(set([sanitize(t) for t in sarah]))[0:3]))
pop() 函数用于移除列表中的一个元素(默认最后一个元素),并且返回该元素的值。obj – 可选参数,要移除列表元素的对象。这里的pop(0)表示删除并返回列表(list)最前面的列表项。
这个程序确实可以达到预期的要求,但是如果是要处理大量的类似的数据,那将是恐怖的事情。
所以我们引入了字典。
使用字典(dictionary)关联数据
对于下面这样的格式
Sarah Sweeney,2002-6-17,2:58,2.58,2:39,2-25,2-55,2:54,2.18,2:55,2:55,2:22,2-21,2.22
我们可以发现对应关系,我们将使用Python字典,将数据值和键关联:
不难发现,Python中的字典与java中的Map是相似的。字典是一个内置的数据结构(内置于Python中),允许数据与键而不是数字关联。这样可以使内存中的数据与实际数据的结构保持一致。字典也可称为”映射”,”散列”或者”关联数组”。
要访问一个名为person的字典中与键Name关联的值,可以使用我们熟悉的中括号记法:
person[‘Name’]
def sanitize(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)
def get_coach_data(filename):
try:
with open(filename) as f:
data = f.readline()
return(data.strip().split(','))
except IOError as ioerr:
print('File error: ' + str(ioerr))
return(None)
sarah = get_coach_data('sarah2.txt')
sarah_data = {}
sarah_data['Name'] = sarah.pop(0)
sarah_data['DOB'] = sarah.pop(0)
sarah_data['Times'] = sarah
print(sarah_data['Name'] + "'s fastest times are: " +
str(sorted(set([sanitize(t) for t in sarah_data['Times']]))[0:3]))
发现尽管使用了字典,代码量还是挺多的,下面来改进一下上面的代码:
def sanitize(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)
def get_coach_data(filename):
try:
with open(filename) as f:
data = f.readline()
templ = data.strip().split(',')
return({'Name' : templ.pop(0),
'DOB' : templ.pop(0),
'Times': str(sorted(set([sanitize(t) for t in templ]))[0:3])})
except IOError as ioerr:
print('File error: ' + str(ioerr))
return(None)
james = get_coach_data('james2.txt')
julie = get_coach_data('julie2.txt')
mikey = get_coach_data('mikey2.txt')
sarah = get_coach_data('sarah2.txt')
print(james['Name'] + "'s fastest times are: " + james['Times'])
print(julie['Name'] + "'s fastest times are: " + julie['Times'])
print(mikey['Name'] + "'s fastest times are: " + mikey['Times'])
print(sarah['Name'] + "'s fastest times are: " + sarah['Times'])
这里可以发现,Python并不需要想java一样先创建对象再返回,在return中,字典创建代码成为函数的一部分。
结果如下:
将代码及其数据打包在类中
与java等其他编程语言一样,Python允许创建并定义面向对象的类,类可以用来将代码与代码处理的数据相关联。
使用类有助于降低复杂性
通过将代码与代码处理的数据相关联,随着代码基的扩大,可以降低复杂性。这样代码中的bug会更好,这意味着更可维护。
定义一个类
Python遵循标准的面向对象编程模式,提供了一种方法允许将代码及其处理的数据定义为一个类。一旦有了类定义,就可以用它来创建(或实例化)数据对象,它会继承类的特性。
在面向对象里,你的代码通常成为类的方法(method),数据通常成为类的属性(attribute)。实例化的数据对象通常称为实例。
使用class定义类
Python使用class创建对象(这一点和java一样)。每个定义的类都有一个特殊的方法,名为init(),可以通过这个方法控制如何初始化对象。
类中的方法与函数的定义很类似,形式如下:
创建对象实例
有了类之后,创建对象实例只需将对类名的调用赋至各个变量。通过这个方法,类(以及init()方法)提供了一种机制,允许你创建一个定制的工厂函数,用来根据需要创建多个对象实例。
与java和c++不同,Python没有定义构造函数”new”的概念.Python会为你完成对象创建,然后你可以使用init()方法定制对象的初始状态。self的重要性
定义一个类时,实际上是在定义一个工厂函数,然后可以在你的代码中使用这工厂函数创建实例:
目标标识符赋至self参数
这是一个非常重要的参数赋值。如果没有这个赋值,Python解释器无法得出方法调用要应用到哪个对象实例。注意,类代码设计为所有对象实例间共享:方法是共享的,而属性不共享。self参数可以帮助标识要处理哪个对象实例的数据。
每个方法的第一个参数都是self
实际上,不仅init()方法需要self作为它的第一个参数,类中定义的所有其他方法也是如此。Python要求每个方法的第一个属性为调用对象实例。
下面扩展这个示例类,在一个名为thing的对象属性中存储一个值,具体指将在初始化时设置。另外还要扩充一个方法, 名为how_big(),它会利用len()BIF返回thing的长度:
在一个对象实例上调用类方法是,Python要求第一个参数是调用对象实例,这往往赋至各方法的self参数。Python解释器会自动完成转换。
看到这里不难发现,Python中的self就是指本身这个类,跟C++和Java中的this 比较类似。
def sanitize(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 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([sanitize(t) for t in self.times]))[0:3])
def get_coach_data(filename):
try:
with open(filename) as f:
data = f.readline()
templ = data.strip().split(',')
return(Athlete(templ.pop(0),templ.pop(0),templ))
except IOError as ioerr:
print('File error: ' + str(ioerr))
return(None)
james = get_coach_data('james2.txt')
julie = get_coach_data('julie2.txt')
mikey = get_coach_data('mikey2.txt')
sarah = get_coach_data('sarah2.txt')
print(james.name + "'s fastest times are: " + str(james.top3()))
print(julie.name + "'s fastest times are: " + str(julie.top3()))
print(mikey.name + "'s fastest times are: " + str(mikey.top3()))
print(sarah.name + "'s fastest times are: " + str(sarah.top3()))
我们在类中定义了类Athlete,并且为它定义了一个方法top3(),值得注意的是, return(sorted(set([sanitize(t) for t in self.times]))[0:3]),这里使用了self.times。
现在我们来增加类中的方法,第一个名为add_time(),将一个额外的计时值追加到选手的计时数据,第二个方法add_times()会用一个或多个计时值(提供为一个列表)来扩展一个选手的计时数据。
def sanitize(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 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([sanitize(t) for t in self.times]))[0:3])
def add_time(self,time_value):
self.times.append(time_value)
def add_times(self,list_of_times):
self.times.extend(list_of_times)
def get_coach_data(filename):
try:
with open(filename) as f:
data = f.readline()
templ = data.strip().split(',')
return(Athlete(templ.pop(0),templ.pop(0),templ))
except IOError as ioerr:
print('File error: ' + str(ioerr))
return(None)
james = get_coach_data('james2.txt')
julie = get_coach_data('julie2.txt')
mikey = get_coach_data('mikey2.txt')
sarah = get_coach_data('sarah2.txt')
print(james.name + "'s fastest times are: " + str(james.top3()))
print(julie.name + "'s fastest times are: " + str(julie.top3()))
print(mikey.name + "'s fastest times are: " + str(mikey.top3()))
print(sarah.name + "'s fastest times are: " + str(sarah.top3()))
结果如下:
注意,方法的第一个参数一定是self。
继承Python内置的list
Python的class允许你从零开始创建一个定制类,就像创建Athlete类那样。不过,class还允许通过继承现有的其他类来创建一个类,这也包括用list,set和dict提供的Python内置数据结构类。通过继承创建的这些类成为子类。从一个现有的类继承时,会为你提供所有现有的方法。
下面来看看如何继承Python的内置list类。
可以看到,NamedList(list)中提供了一个类名list,新类将派生这个类。
list.init([])初始化了所派生的类。定义了NamedList类之后,用它创建一个对象实例,检查对象的类型的类型(使用type()BIF),看看它会提供什么内容(使用dir(BIF))
可以看到johny是一个”NamedList”对象实例。johny可以做列表能做的所有事情,另外还可以在”name”属性中存储数据。
使用list类提供的一些功能来补充johny中存储的数据:
因为johny是一个列表,所以可以对它做列表处理:
好了,我们已经大致了解了继承,现在来对上面的代码进行改进。
def sanitize(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 AthleteList(list):
def __init__(self, a_name, a_dob=None, a_times=[]):
list.__init__([])
self.name = a_name
self.dob = a_dob
self.extend(a_times)
def top3(self):
return(sorted(set([sanitize(t) for t in self]))[0:3])
def get_coach_data(filename):
try:
with open(filename) as f:
data = f.readline()
templ = data.strip().split(',')
return(AthleteList(templ.pop(0), templ.pop(0), templ))
except IOError as ioerr:
print('File error: ' + str(ioerr))
return(None)
james = get_coach_data('james2.txt')
julie = get_coach_data('julie2.txt')
mikey = get_coach_data('mikey2.txt')
sarah = get_coach_data('sarah2.txt')
print(james.name + "'s fastest times are: " + str(james.top3()))
print(julie.name + "'s fastest times are: " + str(julie.top3()))
print(mikey.name + "'s fastest times are: " + str(mikey.top3()))
print(sarah.name + "'s fastest times are: " + str(sarah.top3()))