ch6-定制数据对象(打包代码和数据)

为了看出数据属于哪个选手,教练向各个选手的数据文件中添加了标识数据:选手全名,出生日期,计时数据。

例如:sarah文件的数据更新为:

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

接下来主要考虑Sarah的数据,从教练的原始数据抽取并处理Sarah的3个最快时间。

运行结果确实很好,但是你必须指定并创建Sarah的3个变量,以便标识哪个名字、出生日期和计时数据与Sarah关联。如果再增加代码来处理剩余的3个选手James、Julie和Mikey的数据,这就需要长达12个变量。如果人数增加,变量增加的更多。。。

1、使用字典关联数据

字典:将数据值与键关联。例如:键-值:name-Sarah

字典是一个内置的数据结构(内置于python中),允许将数据与键而不是数字字典关联。这样可以使内存中的数据与实际数据的结构保持一致。

实际上在其他编程语言中“字典”被称为“映射”、“散列”、“关联数组”等。

>>> #字典的具体使用
>>> cleese={}   # 使用大括号创建空字典
>>> p=
SyntaxError: invalid syntax
>>> 
>>> #字典的具体使用
>>> aa={} # 使用大括号创建空字典
>>> bb=dict()   #使用一个工厂函数创建
>>> type(aa)
<class 'dict'>
>>> type(bb)
<class 'dict'>
>>> #向如上的两个字典中分别增加一些数据。
>>> #注意:这里会自行表示出数据的具体结构,因为每个字典都有一个Name和Occupation列表。还要注意bb字典是一次性同时创建的(而不是分步增加数据):
>>> aa['Name']='John aa'
>>> aa['Occupation']=['actor','comedian','writer','film producer']
>>> bb={'Name': 'Michael bb', 'Occupation': ['comedian','actor','writer','tv']}
>>> #数据与键关联后(在这里,键是字符串),可以使用类似列表的记法访问单个数据项
>>> bb['Name']  #使用键作为索引来访问数据
'Michael bb'
>>> aa['Occupation'][-1]  #使用数字访问存储在一个特定字典键位置上的列表项。可以认为是“索引串链”,从右向左读作:“......与Occupation关联的列表的最后一项......”
'film producer'
>>> #与列表一样,字典可以动态扩展来存储额外的键/值对。下面增加一些有关出生地的数据:
>>> bb['出生地']='中国,北京'
>>> aa['出生地']='中国,河南,洛阳'
>>> #与列表不同,字典不会维持插入的顺序,重点是它会维护关联关系,而不是顺序
>>> bb
{'Name': 'Michael bb', 'Occupation': ['comedian', 'actor', 'writer', 'tv'], '出生地': '中国,北京'}
>>> aa
{'Name': 'John aa', 'Occupation': ['actor', 'comedian', 'writer', 'film producer'], '出生地': '中国,河南,洛阳'}
>>> #aa中Occupation的顺序与插入顺序不同,这没关系,不是问题。

下面采用字典来重新保存和处理Sarah的数据:

代码审查和优化:

1、一次性完成字典的创建。

2、把字典创建代码移到get_coach_data()函数中,返回一个字典而不是列表。

3、把确定各个选手的3个最快时间的代码移到get_coach_data()函数中。

4、调整主代码中的函数调用,调用这个新版本的get_coach_data()函数来支持新的操作模式。

把代码和数据放在一起是对的。

尽量将函数和函数所要处理的数据相关联,也就是说,函数将特定于这些数据,而不是一般化的通用函数。

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

类可以用来将代码与代码处理的数据相关联。这么做的好处:

  • 使用类有助于降低复杂性。
  • 降低复杂性意味着bug更少。
  • bug更少意味着代码更可维护。

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

使用class定义类:

class Athlete:
    def __init__(self):
        #初始化各个对象的代码
        #....

定义一个类时,实际上是在定义一个定制工厂函数,然后可以使用这个工厂函数创建实例。

每个定义的类都有一个特殊的方法,名为__init__(),可以通过这个方法控制如何初始化对象

类中的方法同样使用def来定义。

创建对象实例:

a=Athlete()  #只需将对类名的调用赋至各个变量
b=Athlete()
c=Athlete()
d=Athlete()

python 在处理a=Athlete()创建实例的代码时,它把工厂函数调用转换为以下调用,明确了类、方法(自动设置为__init__())和所处理的对象实例:

    Athlete().__init__(a)

可以看出,对象实例的目标标识符a赋至self参数,self参数可以帮助标识要处理哪个对象实例的数据。

注意:类代码设计为在所有对象实例间共享:方法共享,而属性不共享。

类中所有方法的第一个参数都是self。

class Athlete:  #以下注意使用“self”标识调用对象实例
    def __init__(self,value=0):  
        self.thing=value   #“初始化”代码将所提供的一个值赋至一个名为“self.thing”的类属性
    def how_big(self):
        return(len(self.thing))

所以self很重要!!!!

你写的代码:              python执行的代码:

d=Athlete("Andy")      ->     Athlete.__init__(d, "Andy")

d.how_big()           ->      Athlete.how_big(d)

 

Python 3.5.1 (v3.5.1:37a07cee5969, Dec  6 2015, 01:38:48) [MSC v.1900 32 bit (Intel)] on win32
Type "copyright", "credits" or "license()" for more information.
>>> #创建一个名为Athlete的很小的类
>>> class Athlete :
    def __init__(self, a_name, a_dob=None, a_times=[]):
        self.name=a_name
        self.dob=a_dob
        self.times=a_times

        
>>> #创建两个唯一的对象实例
>>> ss=Athlete('ss', '2002-1-1', ['2:58', '2.58', '1.56'])
>>> jj=Athlete('jj')>>> type(ss)
<class '__main__.Athlete'>
>>> type(jj)
<class '__main__.Athlete'>
>>> #尽管ss和jj都是选手且都有Athlete类的工厂函数创建,但他们存储在不同的内存地址上
>>> ss
<__main__.Athlete object at 0x021DB030>
>>> jj
<__main__.Athlete object at 0x021C4F90>
>>> #使用点记法来访问与各个对象实例关联的属性
>>> ss.name
'ss'
>>> ss.dob
'2002-1-1'
>>> ss.times
['2:58', '2.58', '1.56']
>>> jj.name
'jj'
>>> jj.dob
>>> jj.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 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)) #删除字典创建代码,替换为类Athlete对象创建代码
    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()))

更多功能=更多方法!

只需增加方法将你需要的新功能封装在类中;

向类中增加2个方法:第一个add_time()将一个额外的计时值追加到选手的计时数据。第二个方法add_times()会用一个或多个计时值(提供一个列表)来扩展一个选手的计时数据;

已经将代码和数据打包在一起,并且创建了一个定制类,由这个类可以创建共享相同行为的对象。

通过把选手代码和数据封装在一个定制类中,这就创建了一个更可维护的软件。

是的,Athlete类确实很像列表,但是与Python列表的区别只是Athlete类包含name和dob对象的属性。

3、继承Python内置的list

class还允许通过继承现有的其他类来创建一个类,通过继承创建的称为子类,子类会免费享有父类所有的功能;

抛弃之前的Athlete类,从内置的list类继承其大量的功能。

Python 3.5.1 (v3.5.1:37a07cee5969, Dec  6 2015, 01:38:48) [MSC v.1900 32 bit (Intel)] on win32
Type "copyright", "credits" or "license()" for more information.
>>> 
===== RESTART: D:\workspace\headfirstpython\chapter6\page200\page196.py =====
['1.31']
['1.21', '1.31', '2.22']
>>> #如何继承Python的内置list类
>>> #首先派生内置list类创建一个定制列表,它还有一个名为name的属性
>>> class NamedList(list):
    def __init__(self,a_name):
        list.__init__([ ])  #初始化所派生的类,然后把参数赋至属性
        self.name=a_name

>>> 
>>> jy=NamedList
>>>  jy=NamedList('john paul jones')#创建一个新的‘NameList’对象实例
 
SyntaxError: unexpected indent
>>> jy=NamedList('john paul jones')#创建一个新的‘NameList’对象实例
>>> type(jy)
<class '__main__.NamedList'>
>>> dir(jy)  #查看jy能够提供的内容
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__module__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'name', 'pop', 'remove', 'reverse', 'sort']
>>> #jy可以做列表可以做的所有事情,还可以在‘name’属性中存储数据
>>> #下面使用内置list提供的方法为‘NameList’增加数据
>>> jy.append('bass player')
>>> jy.extend(['cc','aa','mm'])
>>> jy
['bass player', 'cc', 'aa', 'mm']
>>> jy,name
Traceback (most recent call last):
  File "<pyshell#18>", line 1, in <module>
    jy,name
NameError: name 'name' is not defined
>>> jy.name
'john paul jones'
>>> #因jy是一个列表,完全可以对它做列表处理
>>> for attr in jy:
    print ( jy.name+"is a "+attr +"." )

    
john paul jonesis a bass player.
john paul jonesis a cc.
john paul jonesis a aa.
john paul jonesis a mm.
>>> 
>>> 

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()))
Python 3.5.1 (v3.5.1:37a07cee5969, Dec  6 2015, 01:38:48) [MSC v.1900 32 bit (Intel)] on win32
Type "copyright", "credits" or "license()" for more information.
>>> 
===== RESTART: D:\workspace\headfirstpython\chapter6\page207\page210.py =====
James Lee's fastest times are: ['2.01', '2.16', '2.22']
Julie Jones's fastest times are: ['2.11', '2.23', '2.59']
Mikey McManus's fastest times are: ['2.22', '2.31', '2.38']
Sarah Sweeney's fastest times are: ['2.18', '2.21', '2.22']
>>> 

通过将类建立在内置的功能之上,你充分利用了Python数据结构的强大功能,同时还提供了具体应用需要的定制解决方案。

这是一个更可维护的解决方案。

 

posted @ 2016-04-28 17:52  垄上行  阅读(226)  评论(0编辑  收藏  举报