现有4个文本文件,分别记录James,Sarah,Julie和Mikey在体育方面的时间数据。
首先先将各个文件将数据读入各自的列表,并在屏幕上显示这些列表
with open('james.txt') as jaf:
data=jaf.readline()
james=data.strip().split(',')
with open('julie.txt') as juf:
data=juf.readline()
julie=data.strip().split(',')
with open('mikey.txt') as mif:
data=mif.readline()
mikey=data.strip().split(',')
with open('sarah.txt') as saf:
data=saf.readline()
sarah=data.strip().split(',')
print(james)
print(julie)
print(mikey)
print(sarah)
结果如下:
排序
使用Python对数据排序时,你有两种选择。
原地排序(In-place sorting)
按你指定的顺序排列数据,然后用排序后的数据替换原来的数据,原来的顺序会丢失。对于列表,sort()方法会提供原地排序。
复制排序(Copied sorting)
按你指定的顺序排列数据,然后返回原数据的一个有序副本。原数据的顺序依然保留,只是对一个副本排序。在Python中,sorted()BIF支持复制排序。
下面对四人的数据进行排序,上面的代码改为:
with open('james.txt') as jaf:
data=jaf.readline()
james=data.strip().split(',')
with open('julie.txt') as juf:
data=juf.readline()
julie=data.strip().split(',')
with open('mikey.txt') as mif:
data=mif.readline()
mikey=data.strip().split(',')
with open('sarah.txt') as saf:
data=saf.readline()
sarah=data.strip().split(',')
print(sorted(james))
print(sorted(julie))
print(sorted(mikey))
print(sorted(sarah))
print(sorted(james))是函数串链(function chaining)。
我们已经见过方法串链(比如data.strip().split(‘:’)),用于把多个方法串连在一起,生成所需的结果,执行顺序是从左往右。
函数串链允许对数据应用一系列函数。每个函数会取得数据,对它完成某个操作,然后把转换后的数据继续向下传递到下一个函数。与方法串链不同,函数串链要从右向左读。
出来的结果为:
我们发现结果并不是我们想要的,数据中有-和,和:几种,数据中存在的不一致行导致了排序失败。
下面我们定义一个函数sanitize()来解决这个问题。这个函数将各个选手的列表接受一个字符串作为输入,然后处理这个字符串,将找到的所有-和:替换为点号,并返回清理过的字符串。注意,如果字符串已经包含一个点号,则不需要再做清理。
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)
只有sanitize()函数还不够,还需要迭代处理每一个出局列表,使用这个新函数将每个选手的时间分别转换为正确的格式。
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)
with open('james.txt') as jaf:
data=jaf.readline()
james=data.strip().split(',')
with open('julie.txt') as juf:
data=juf.readline()
julie=data.strip().split(',')
with open('mikey.txt') as mif:
data=mif.readline()
mikey=data.strip().split(',')
with open('sarah.txt') as saf:
data=saf.readline()
sarah=data.strip().split(',')
clean_james=[]
clean_julie=[]
clean_mikey=[]
clean_sarah=[]
for each_t in james:
clean_james.append(sanitize(each_t))
for each_t in julie:
clean_julie.append(sanitize(each_t))
for each_t in mikey:
clean_mikey.append(sanitize(each_t))
for each_t in sarah:
clean_sarah.append(sanitize(each_t))
print(sorted(clean_james,reverse=True))
print(sorted(clean_julie))
print(sorted(clean_mikey))
print(sorted(clean_sarah))
结果如下:
这个结果格式一致,符合我们的预期。并且发现james的排序是降序的。这是因为传入了参数reverse=True。
注意:
默认地,sort()和sorted()BIF都会按升序对数据排序,要以降序对数据排序,需要向sort()或sorted()传入参数reverse=True。
推导列表
我们会发现,上面的代码创建了4个列表来保存从数据文件读取的数据,然后代码在创建另外4个列表保存经过清理的数据。如果列表数量很多的时候会是个十分巨大的工作量。应该还有更好的方法来写代码。
转换列表是一个很常见的需求,因此Python提供了一个工具,可以尽可能毫不费力地完成这种转换,名为列表推导(list comprehension)。设计列表推导式为了减少一个列表转换为另一个列表时所需编写的代码量。
一个列表转换为另一个列表时,要做4件事:
①创建一个新列表来存放转换后的数据
②迭代处理原列表中的各个数据项
③每次迭代时完成转换
④将转换后的数据追加到新列表
clean_james = []
for each_t in james:
clean_james.append(sanitize(each_t))
列表推导完成同样的功能
clean_james=[sanitize(each_t) for each_t in james]
4件事缩减为一行代码,不再需要指定使用append()方法,因为这个动作已经隐含在列表推导中了。
Python的列表推导式这种语言支持函数编程概念的一个例子。
定义列表推导时,到当心在哪里使用sorted()BIF。
千万不可再列表推导中用函数链sorted(sanitize(t))。因为一次只会对一个列表项完成转换,而不是对整个列表。
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)
with open('james.txt') as jaf:
data=jaf.readline()
james=data.strip().split(',')
with open('julie.txt') as juf:
data=juf.readline()
julie=data.strip().split(',')
with open('mikey.txt') as mif:
data=mif.readline()
mikey=data.strip().split(',')
with open('sarah.txt') as saf:
data=saf.readline()
sarah=data.strip().split(',')
print(sorted([sanitize(t) for t in james]))
print(sorted([sanitize(t) for t in julie]))
print(sorted([sanitize(t) for t in mikey]))
print(sorted([sanitize(t) for t in sarah]))
结果为:
可以看出这极大地的减少了代码量。
迭代删除重复项
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)
with open('james.txt') as jaf:
data=jaf.readline()
james=data.strip().split(',')
with open('julie.txt') as juf:
data=juf.readline()
julie=data.strip().split(',')
with open('mikey.txt') as mif:
data=mif.readline()
mikey=data.strip().split(',')
with open('sarah.txt') as saf:
data=saf.readline()
sarah=data.strip().split(',')
james=sorted([sanitize(t) for t in james])
julie=sorted([sanitize(t) for t in julie])
mikey=sorted([sanitize(t) for t in mikey])
sarah=sorted([sanitize(t) for t in sarah])
unique_james=[]
for each_t in james:
if each_t not in unique_james:
unique_james.append(each_t)
print(unique_james[0:3])
unique_julie=[]
for each_t in julie:
if each_t not in unique_julie:
unique_julie.append(each_t)
print(unique_julie[0:3])
unique_mikey=[]
for each_t in mikey:
if each_t not in unique_mikey:
unique_mikey.append(each_t)
print(unique_mikey[0:3])
unique_sarah=[]
for each_t in sarah:
if each_t not in unique_sarah:
unique_sarah.append(each_t)
print(unique_sarah[0:3])
这里把列表推导的结果存入原本的列表中,然后各自新建一个列表,首先判断每一个项是否已包含在新列表中,不在才加入。
结果为:
尽管结果看起来是符合我们的要求,但是这种办法代码量重复,不是明智的选择。
用集合项删除重复项
Python提供了集合数据结构。它类似于数学中的集合。
Python中集合(Set)最突出的特性是集合中的数据项是无序的,而且不允许重复,如果试图向一个集合增加一个数据项,而该集合中已经包含有这个数据项,Python就会将其忽略。这一点和java中的Set是一致的。
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)
with open('james.txt') as jaf:
data=jaf.readline()
james=data.strip().split(',')
with open('julie.txt') as juf:
data=juf.readline()
julie=data.strip().split(',')
with open('mikey.txt') as mif:
data=mif.readline()
mikey=data.strip().split(',')
with open('sarah.txt') as saf:
data=saf.readline()
sarah=data.strip().split(',')
print(sorted(set([sanitize(t) for t in james]))[0:3])
print(sorted(set([sanitize(t) for t in julie]))[0:3])
print(sorted(set([sanitize(t) for t in mikey]))[0:3])
print(sorted(set([sanitize(t) for t in sarah]))[0:3])
分片——从一个列表访问多个列表项,如list[3:6],这会访问列表中从索引位置3直到索引位置6(但不包括)的列表项。
将上面的代码简化,将四个with语句改为一个函数。
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)
james=get_coach_data('james.txt')
julie=get_coach_data('julie.txt')
mikey=get_coach_data('mikey.txt')
sarah=get_coach_data('sarah.txt')
print(sorted(set([sanitize(t) for t in james]))[0:3])
print(sorted(set([sanitize(t) for t in julie]))[0:3])
print(sorted(set([sanitize(t) for t in mikey]))[0:3])
print(sorted(set([sanitize(t) for t in sarah]))[0:3])
结果不变。