定制数据对象

现有类似这样的数据
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有名为head的库吗_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中,字典创建代码成为函数的一部分。

结果如下:

python有名为head的库吗_Python_02


将代码及其数据打包在类中

与java等其他编程语言一样,Python允许创建并定义面向对象的类,类可以用来将代码与代码处理的数据相关联。

使用类有助于降低复杂性
通过将代码与代码处理的数据相关联,随着代码基的扩大,可以降低复杂性。这样代码中的bug会更好,这意味着更可维护。

定义一个类
Python遵循标准的面向对象编程模式,提供了一种方法允许将代码及其处理的数据定义为一个类。一旦有了类定义,就可以用它来创建(或实例化)数据对象,它会继承类的特性。

在面向对象里,你的代码通常成为类的方法(method),数据通常成为类的属性(attribute)。实例化的数据对象通常称为实例。

使用class定义类
Python使用class创建对象(这一点和java一样)。每个定义的类都有一个特殊的方法,名为init(),可以通过这个方法控制如何初始化对象。

类中的方法与函数的定义很类似,形式如下:

python有名为head的库吗_python有名为head的库吗_03

创建对象实例

有了类之后,创建对象实例只需将对类名的调用赋至各个变量。通过这个方法,类(以及init()方法)提供了一种机制,允许你创建一个定制的工厂函数,用来根据需要创建多个对象实例。

python有名为head的库吗_python有名为head的库吗_04


与java和c++不同,Python没有定义构造函数”new”的概念.Python会为你完成对象创建,然后你可以使用init()方法定制对象的初始状态。self的重要性

定义一个类时,实际上是在定义一个工厂函数,然后可以在你的代码中使用这工厂函数创建实例:

python有名为head的库吗_Python_05

目标标识符赋至self参数
这是一个非常重要的参数赋值。如果没有这个赋值,Python解释器无法得出方法调用要应用到哪个对象实例。注意,类代码设计为所有对象实例间共享:方法是共享的,而属性不共享。self参数可以帮助标识要处理哪个对象实例的数据。

每个方法的第一个参数都是self
实际上,不仅init()方法需要self作为它的第一个参数,类中定义的所有其他方法也是如此。Python要求每个方法的第一个属性为调用对象实例。

下面扩展这个示例类,在一个名为thing的对象属性中存储一个值,具体指将在初始化时设置。另外还要扩充一个方法, 名为how_big(),它会利用len()BIF返回thing的长度:

python有名为head的库吗_数据_06

在一个对象实例上调用类方法是,Python要求第一个参数是调用对象实例,这往往赋至各方法的self参数。Python解释器会自动完成转换。

python有名为head的库吗_Python_07

看到这里不难发现,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()))

结果如下:

python有名为head的库吗_python_08

注意,方法的第一个参数一定是self。


继承Python内置的list
Python的class允许你从零开始创建一个定制类,就像创建Athlete类那样。不过,class还允许通过继承现有的其他类来创建一个类,这也包括用list,set和dict提供的Python内置数据结构类。通过继承创建的这些类成为子类。从一个现有的类继承时,会为你提供所有现有的方法。

下面来看看如何继承Python的内置list类。

python有名为head的库吗_数据_09


可以看到,NamedList(list)中提供了一个类名list,新类将派生这个类。

list.init([])初始化了所派生的类。定义了NamedList类之后,用它创建一个对象实例,检查对象的类型的类型(使用type()BIF),看看它会提供什么内容(使用dir(BIF))

python有名为head的库吗_数据_10

可以看到johny是一个”NamedList”对象实例。johny可以做列表能做的所有事情,另外还可以在”name”属性中存储数据。

使用list类提供的一些功能来补充johny中存储的数据:

python有名为head的库吗_数据_11

因为johny是一个列表,所以可以对它做列表处理:

python有名为head的库吗_Python_12

好了,我们已经大致了解了继承,现在来对上面的代码进行改进。

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()))